diff --git a/HypoAlarm/HypoAlarm-HypoAlarm.iml b/HypoAlarm/HypoAlarm-HypoAlarm.iml new file mode 100644 index 0000000..2d8cdeb --- /dev/null +++ b/HypoAlarm/HypoAlarm-HypoAlarm.iml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HypoAlarm/build.gradle b/HypoAlarm/build.gradle new file mode 100644 index 0000000..da51134 --- /dev/null +++ b/HypoAlarm/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'android' + +android { + compileSdkVersion 19 + buildToolsVersion "19.0.1" + + defaultConfig { + minSdkVersion 10 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + compile 'com.android.support:support-v4:19.0.1' + compile 'com.android.support:appcompat-v7:19.0.1' + compile fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/HypoAlarm/proguard-rules.txt b/HypoAlarm/proguard-rules.txt new file mode 100644 index 0000000..60fff78 --- /dev/null +++ b/HypoAlarm/proguard-rules.txt @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/tim/sources/android-studio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} \ No newline at end of file diff --git a/HypoAlarm/src/main/AndroidManifest.xml b/HypoAlarm/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4a9f3a9 --- /dev/null +++ b/HypoAlarm/src/main/AndroidManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HypoAlarm/src/main/ic_launcher-web.png b/HypoAlarm/src/main/ic_launcher-web.png new file mode 100644 index 0000000..0023ef9 Binary files /dev/null and b/HypoAlarm/src/main/ic_launcher-web.png differ diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java new file mode 100644 index 0000000..0128854 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java @@ -0,0 +1,92 @@ +package za.org.treehouse.hypoalarm; + +import android.app.Activity; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.Vibrator; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; + +import java.util.Timer; +import java.util.TimerTask; + +// TODO See GlowPad. + +public class AlarmAlertActivity extends Activity { + // TODO correct life + static int ALERT_LIFE = 1000*10;//1000*60*2; // 2 minutes + private Timer timer; + private PowerManager.WakeLock fullWl; + private AlarmManager graceManager; + private PendingIntent gracePendingIntent; + private Vibrator vibrator; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.alarm_alert); + + // Disable any current notifications + this.stopService(new Intent(getApplicationContext(), AlarmNotify.class)); + + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON ); + + // Turn off the alert activity, and switch to a notification + timer = new Timer(); + timer.schedule(new TimerTask() { + public void run() { + // Switch to notification + startService(new Intent(getApplicationContext(), AlarmNotify.class)); + finish(); + Log.d("AlarmAlertActivity", "Stopped AlarmAlertActivity"); + } + }, ALERT_LIFE); + } + + @Override + public void onStart() { + super.onStart(); + + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + fullWl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK + | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "AlarmAlertActivity"); + fullWl.acquire(); + + long[] pattern = {0, 1000, 1000}; + vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(pattern, 0); + + // TODO change button to slide... + // TODO change slide to circle slide? https://github.com/JesusM/HoloCircleSeekBar + Button cancelButton = (Button) findViewById(R.id.cancel_dialog_button); + cancelButton.setOnClickListener (new View.OnClickListener() { + @Override + public void onClick(View view) { + graceManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + Intent graceIntent = new Intent(getApplicationContext(), GraceReceiver.class); + gracePendingIntent = PendingIntent.getBroadcast(getApplicationContext(), MainActivity.GRACE_REQUEST, graceIntent, 0); + graceManager.cancel(gracePendingIntent); + Log.d("AlarmAlertActivity", "Cancelled grace alarm."); + finish(); + } + }); + } + + public void onStop() { + timer.cancel(); + vibrator.cancel(); + fullWl.release(); + super.onStop(); + } + +} \ No newline at end of file diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java new file mode 100644 index 0000000..bf803d1 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java @@ -0,0 +1,73 @@ +package za.org.treehouse.hypoalarm; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.PowerManager; +import android.preference.PreferenceManager; +import android.util.Log; + +import java.util.Calendar; + +// TODO sound audible alarm -- see AlarmKlaxon.java +// TODO Lower alarm volume if in phone call (and detect phone call!) +// TODO set another alarm for the next half-hour (or grace_period / 2)? + +public class AlarmReceiver extends BroadcastReceiver { + SharedPreferences sharedPref; + private AlarmManager alarmManager, graceManager; + private PendingIntent alarmPendingIntent, gracePendingIntent; + + @Override + public void onReceive(Context context, Intent intent) { + sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + Boolean alarmActive = sharedPref.getBoolean(context.getString(R.string.AlarmActivePref), true); + + if (alarmActive) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock partialWl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AlarmReceiver"); + partialWl.acquire(); + + // Disable any current notifications + //context.stopService(new Intent(context, AlarmNotify.class)); + + // Set a grace period alarm to send SMS + int gracePeriod = sharedPref.getInt(context.getString(R.string.GracePeriodPref), 60); + // TODO remove this + gracePeriod = 1; + + Calendar graceCal = Calendar.getInstance(); + graceCal.set(Calendar.SECOND, 0); + graceCal.add(Calendar.MINUTE, gracePeriod); + graceManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent graceIntent = new Intent(context, GraceReceiver.class); + gracePendingIntent = PendingIntent.getBroadcast(context, MainActivity.GRACE_REQUEST, graceIntent, 0); + graceManager.cancel(gracePendingIntent); + graceManager.setExact(AlarmManager.RTC_WAKEUP, graceCal.getTimeInMillis(), gracePendingIntent); + Log.d("AlarmReceiver", "Setting grace alarm for " + MainActivity.debugDate(graceCal)); + + // Allow user to acknowledge alarm and cancel grace alarm + Intent alertActivityIntent = new Intent(context, AlarmAlertActivity.class); + alertActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + context.startActivity(alertActivityIntent); + + + // Reset for tomorrow; as of API 19, setRepeating() is inexact, so we use setExact() + String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null); + // Calendar automatically advances the day since alarmTimeStr is now in the past. + Calendar cal = MainActivity.TimeStringToCalendar(alarmTimeStr); + alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmPendingIntent = PendingIntent.getBroadcast(context, MainActivity.ALARM_REQUEST, intent, 0); + alarmManager.cancel(alarmPendingIntent); + // TODO use set() for older APIs + alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmPendingIntent); + Log.d("AlarmReceiver", "Resetting alarm for " + MainActivity.debugDate(cal)); + + partialWl.release(); + } + } +} diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/BootReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/BootReceiver.java new file mode 100644 index 0000000..20fe279 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/BootReceiver.java @@ -0,0 +1,36 @@ +package za.org.treehouse.hypoalarm; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import java.util.Calendar; + +public class BootReceiver extends BroadcastReceiver { + SharedPreferences sharedPref; + private AlarmManager alarmManager; + private PendingIntent alarmIntent; + + @Override + public void onReceive(Context context, Intent intent) { + sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { + // Reset for tomorrow; as of API 19, setRepeating() is inexact, so we use setExact() + String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null); + if (alarmTimeStr != null) { + // If it's later than alarmTimeStr, Calendar automatically advances the day. + Calendar cal = MainActivity.TimeStringToCalendar(alarmTimeStr); + alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + // TODO use set() for older APIs + alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmIntent); + Log.d("BootReceiver", "Setting alarm for "+MainActivity.debugDate(cal)); + } + } + } +} diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java new file mode 100644 index 0000000..f3c8aeb --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java @@ -0,0 +1,36 @@ +package za.org.treehouse.hypoalarm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.PowerManager; +import android.preference.PreferenceManager; +import android.telephony.SmsManager; +import android.util.Log; + +public class GraceReceiver extends BroadcastReceiver { + SharedPreferences sharedPref; + + @Override + public void onReceive(Context context, Intent intent) { + sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + Boolean alarmActive = sharedPref.getBoolean(context.getString(R.string.AlarmActivePref), true); + + if (alarmActive) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock partialWl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AlarmReceiver"); + partialWl.acquire(); + + String phoneNumber = sharedPref.getString(context.getString(R.string.PhoneNumberPref), null); + String message = sharedPref.getString(context.getString(R.string.MessagePref), null); + + SmsManager sms = SmsManager.getDefault(); + // TODO uncomment this: + //sms.sendTextMessage(phoneNumber, null, message, null, null); + Log.d("GraceReceiver", "Sending sms to " + phoneNumber); + + partialWl.release(); + } + } +} diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java new file mode 100644 index 0000000..4ca3ab4 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java @@ -0,0 +1,495 @@ +package za.org.treehouse.hypoalarm; + +import android.app.AlarmManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.PendingIntent; +import android.app.TimePickerDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.provider.ContactsContract; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBarActivity; +import android.text.InputType; +import android.text.format.DateFormat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.Switch; +import android.widget.TimePicker; +import android.widget.Toast; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// Settings: alarm time, phone number, custom message, grace period length +// Set alarm +// Display alarm notification +// Display countdown (in seconds) or time till alarm fires (in minutes) +// Reset alarm +// Send SMS + +// Possible settings: +// More than one phone number? +// Alerts via Whatsapp and other protocols? + +// TODO: klaxon +// TODO: glowpad dismissal (or circular seekbar) + +public class MainActivity extends ActionBarActivity { + static int ALARM_REQUEST = 1; + static int GRACE_REQUEST = 2; + static int CANCEL_GRACE_REQUEST = 3; + static Switch alarmActiveSwitch; + static Button alarmTimeButton; + static Spinner gracePeriodSpinner; + static EditText phoneNumberButton; + static EditText messageButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .add(R.id.container, new MainFragment()) + .commit(); + } + } + + // Main screen + public static class MainFragment extends Fragment { + + public MainFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.fragment_main, container, false); + return rootView; + } + + public void onStart() { + super.onStart(); + + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + // Allow alarm to activate + Boolean alarmActive = sharedPref.getBoolean(getString(R.string.AlarmActivePref), true); + alarmActiveSwitch = (Switch) getActivity().findViewById(R.id.alarm_active_switch); + alarmActiveSwitch.setChecked(alarmActive); + alarmActiveSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + AlarmManager alarmManager, graceManager; + PendingIntent alarmPendingIntent, gracePendingIntent; + SharedPreferences.Editor editor = sharedPref.edit(); + + editor.putBoolean(getString(R.string.AlarmActivePref), b); + editor.commit(); + + if (!b) { + // Cancel any current alarm + alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(getActivity(), AlarmReceiver.class); + alarmPendingIntent = PendingIntent.getBroadcast(getActivity(), ALARM_REQUEST, alarmIntent, 0); + alarmManager.cancel(alarmPendingIntent); + // Cancel any grace period + graceManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); + Intent graceIntent = new Intent(getActivity(), GraceReceiver.class); + gracePendingIntent = PendingIntent.getBroadcast(getActivity(), GRACE_REQUEST, graceIntent, 0); + graceManager.cancel(gracePendingIntent); + // Stop any notifications + getActivity().stopService(new Intent(getActivity(), AlarmNotify.class)); + Log.d("MainActivity", "Alarms cancelled"); + Toast.makeText(getActivity().getApplicationContext(), getString(R.string.alarmCancelled), Toast.LENGTH_SHORT).show(); + } else { + // Activate the alarm + DialogFragment alarmFragment = new TimePickerFragment(); + alarmFragment.show(getActivity().getSupportFragmentManager(), "alarmTimePicker"); + alarmFragment.dismiss(); + } + } + }); + + // Set alarm time + String defaultTimeStr = "09:00"; + String alarmTimeStr = verifyTimeString(sharedPref.getString(getString(R.string.AlarmTimePref), defaultTimeStr)); + alarmTimeButton = (Button) getActivity().findViewById(R.id.alarm_time); + + // TODO remove this testing stuff + Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, 1); + alarmTimeStr = CalendarToTimeString(c); + + alarmTimeButton.setText(alarmTimeStr); + alarmTimeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogFragment alarmFragment = new TimePickerFragment(); + alarmFragment.show(getActivity().getSupportFragmentManager(), "alarmTimePicker"); + } + }); + + // TODO remove these simulated clicks + DialogFragment alarmFragment = new TimePickerFragment(); + alarmFragment.show(getActivity().getSupportFragmentManager(), "alarmTimePicker"); + alarmFragment.dismiss(); + + + // Set grace period + int defaultGrace = 60; + int gracePeriod = sharedPref.getInt(getString(R.string.GracePeriodPref), defaultGrace); + gracePeriodSpinner = (Spinner) getActivity().findViewById(R.id.grace_period); + gracePeriodSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + Object value = parent.getItemAtPosition(pos); + SharedPreferences.Editor editor = sharedPref.edit(); + int minutes = GracePeriodToMinutes(value.toString()); + editor.putInt(getString(R.string.GracePeriodPref), minutes); + editor.commit(); + /* + CharSequence text = "Grace period is " + GracePeriodToMinutes(value.toString()) + " or " + MinutesToGracePeriodStr(GracePeriodToMinutes(value.toString()))+ "."; + Toast.makeText(getActivity().getApplicationContext(), text, Toast.LENGTH_SHORT).show(); + */ + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + // Set value of drop-down list to value of gracePeriodStr + ArrayAdapter gracePeriodSpinnerAdapter = (ArrayAdapter) gracePeriodSpinner.getAdapter(); + int spinnerPosition = gracePeriodSpinnerAdapter.getPosition(MinutesToGracePeriodStr(gracePeriod)); + gracePeriodSpinner.setSelection(spinnerPosition); + + // Set phone number + String phoneNumberStr = sharedPref.getString(getString(R.string.PhoneNumberPref), null); + phoneNumberButton = (EditText) getActivity().findViewById(R.id.phone_number); + phoneNumberButton.setText(phoneNumberStr); + phoneNumberButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogFragment phoneFragment = new PhonePickerFragment(); + phoneFragment.show(getActivity().getSupportFragmentManager(), "phoneNumberPicker"); + } + }); + + // Set message + String messageStr = sharedPref.getString(getString(R.string.MessagePref), getString(R.string.defaultMessage)); + messageButton = (EditText) getActivity().findViewById(R.id.message); + messageButton.setText(messageStr); + messageButton.setOnClickListener(new View.OnClickListener() { + SharedPreferences sharedPref; + + @Override + public void onClick(View view) { + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + String messageStr = sharedPref.getString(getString(R.string.MessagePref), getString(R.string.defaultMessage)); + + final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + + final EditText messageInput = new EditText(getActivity()); + messageInput.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_MULTI_LINE | + InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | + InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + if (messageButton != null) { + messageInput.setText(messageButton.getText().toString()); + } + messageInput.setSelection(messageInput.getText().length()); + alert.setView(messageInput); + alert.setMessage(getString(R.string.setMessage)); + + + alert.setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String value = messageInput.getText().toString(); + if (value.compareTo("") == 0) { + value = getString(R.string.defaultMessage); + } + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(getString(R.string.MessagePref), value); + editor.commit(); + messageButton.setText(value); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + }); + alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + // Canceled. + } + }); + alert.show(); + messageInput.requestFocus(); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + }); + } + } + + // Alarm time picker + public static class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener { + SharedPreferences sharedPref; + private AlarmManager alarmManager; + private PendingIntent alarmPendingIntent; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + // Use the current set time as the default value for the picker + //String defaultTimeStr = alarmTimeButton.getText().toString(); + //String alarmTimeStr = sharedPref.getString(getString(R.string.AlarmTimePref), defaultTimeStr); + String alarmTimeStr = alarmTimeButton.getText().toString(); + Calendar cal = TimeStringToCalendar(alarmTimeStr); + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + + // Create a new instance of TimePickerDialog and return it + return new TimePickerDialog(getActivity(), this, hour, minute, + DateFormat.is24HourFormat(getActivity())); + } + + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + String alarmStr = HourMinuteToTimeString(hourOfDay, minute); + Boolean alarmActive = sharedPref.getBoolean(getString(R.string.AlarmActivePref), true); + + // Set time preference + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(getString(R.string.AlarmTimePref), alarmStr); + editor.commit(); + + Button alarm_time = (Button) getActivity().findViewById(R.id.alarm_time); + alarm_time.setText(alarmStr); + + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(System.currentTimeMillis()); + cal.set(Calendar.HOUR_OF_DAY, hourOfDay); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, 0); + + if (alarmActive) { + // Initialise alarm, which displays a dialog and system alert, and + // calls CountDownTimer (or AlarmManager?) with grace_period as the delay + // which in turn, sends SMS if dialog is not exited. + alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(getActivity(), AlarmReceiver.class); + alarmPendingIntent = PendingIntent.getBroadcast(getActivity(), ALARM_REQUEST, alarmIntent, 0); + // Cancel any existing alarms + alarmManager.cancel(alarmPendingIntent); + // Set or reset alarm + // TODO use set() for older APIs + alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmPendingIntent); + Log.d("MainActivity", "Setting alarm for " + MainActivity.debugDate(cal)); + + // Set boot receiver, so alarm restarts on boot + ComponentName receiver = new ComponentName(getActivity(), BootReceiver.class); + PackageManager pm = getActivity().getPackageManager(); + pm.setComponentEnabledSetting(receiver, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + + // Display toast + CharSequence text = getString(R.string.alarmSetToast) + " " + alarmStr; + Toast.makeText(getActivity().getApplicationContext(), text, Toast.LENGTH_SHORT).show(); + } + } + } + + public static class PhonePickerFragment extends DialogFragment { + SharedPreferences sharedPref; + EditText phoneButton; + EditText phoneInput; + Button contactsButton; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setMessage(R.string.setPhoneNumber); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.phone_dialog, null); + alert.setView(dialogView); + + phoneButton = (EditText) getActivity().findViewById(R.id.phone_number); + phoneInput = (EditText) dialogView.findViewById(R.id.dialog_phone_number); + phoneInput.setText(phoneButton.getText().toString()); + phoneInput.setSelection(phoneInput.getText().length()); + contactsButton = (Button) dialogView.findViewById(R.id.dialog_contacts_button); + + alert.setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String value = phoneInput.getText().toString(); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(getString(R.string.PhoneNumberPref), value); + editor.commit(); + phoneButton.setText(value); + } + }); + alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + // Cancelled + } + }); + contactsButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); + intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE); + startActivityForResult(intent, 1); + } + }); + return alert.create(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (data != null) { + Uri uri = data.getData(); + if (uri != null) { + Cursor c = null; + try { + c = getActivity().getContentResolver().query(uri, new String[]{ + ContactsContract.CommonDataKinds.Phone.NUMBER, + ContactsContract.CommonDataKinds.Phone.TYPE }, + null, null, null); + + if (c != null && c.moveToFirst()) { + String number = c.getString(0); + phoneInput.setText(number); + } + } finally { + if (c != null) { + c.close(); + } + } + } + } + } + + } + + public static String verifyTimeString(String timeStr) { + return CalendarToTimeString(TimeStringToCalendar(timeStr)); + } + public static String HourMinuteToTimeString(int hour, int minute) { + return CalendarToTimeString(TimeStringToCalendar(hour + ":" + minute)); + } + + public static Calendar TimeStringToCalendar(String dateString) { + Date date; + Calendar cal; + final String FORMAT="HH:mm"; // z = time zone + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT, Locale.getDefault()); + sdf.format(new Date()); + try { + date = sdf.parse(dateString); + // date has the correct hour and minute, but the incorrect day, + // month and year information. To get information out of it, we need to + // convert it to a Calendar. + Calendar dateCal = Calendar.getInstance(); + dateCal.setTime(date); + + // Create a new calendar with the correct day, month and year, + // and set the hour and minute by hand. + cal = Calendar.getInstance(); + cal.set(Calendar.HOUR, dateCal.get(Calendar.HOUR)); + cal.set(Calendar.MINUTE, dateCal.get(Calendar.MINUTE)); + cal.set(Calendar.SECOND, 0); + // Ensure that this date is in the future + if (date.before(new Date())) { + cal.add(Calendar.DAY_OF_MONTH, 1); + } + return cal; + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + public static String CalendarToTimeString(Calendar cal) { + final String FORMAT="HH:mm"; + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT, Locale.getDefault()); + return sdf.format(cal.getTime()); + } + public static String debugDate(Calendar cal) { + SimpleDateFormat print = new SimpleDateFormat("dd-MM-yyyy HH:mm:ssZ"); + return print.format(cal.getTime()); + } + public static int GracePeriodToMinutes(String gracePeriod) { + Pattern p = Pattern.compile("(?:(\\d+)\\s+hours?)?\\s*(?:(\\d+)\\s+minutes?)?"); + + Matcher m = p.matcher(gracePeriod); + if (m.find()) { + int hours = 0; + int minutes = 0; + if (m.group(1) != null) { + hours = Integer.parseInt(m.group(1)); + } + if (m.group(2) != null) { + minutes = Integer.parseInt(m.group(2)); + } + minutes = minutes + (hours * 60); + return minutes; + } + return 0; + + } + public static String MinutesToGracePeriodStr(int minutes) { + int hours = minutes / 60; + int remMinutes = minutes % 60; + + String hourStr = " hours"; + if (hours == 1) { + hourStr = " hour"; + } + + String minStr = " minutes"; + if (remMinutes == 1) { + minStr = " minute"; + } + + if (hours > 0) { + if (remMinutes == 0) { + return hours + hourStr; + } + return hours + hourStr + " " + remMinutes + minStr; + } + return remMinutes + minStr; + } + + +} diff --git a/HypoAlarm/src/main/res/drawable-hdpi/alarm_notification.png b/HypoAlarm/src/main/res/drawable-hdpi/alarm_notification.png new file mode 100644 index 0000000..ed2d3c5 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-hdpi/alarm_notification.png differ diff --git a/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..32b703a Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_glowdot.png b/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_glowdot.png new file mode 100644 index 0000000..983c45e Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_glowdot.png differ diff --git a/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_handle_pressed.png b/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_handle_pressed.png new file mode 100644 index 0000000..58a5f16 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_handle_pressed.png differ diff --git a/HypoAlarm/src/main/res/drawable-mdpi/alarm_notification.png b/HypoAlarm/src/main/res/drawable-mdpi/alarm_notification.png new file mode 100644 index 0000000..2822a92 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-mdpi/alarm_notification.png differ diff --git a/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..23abd3e Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_glowdot.png b/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_glowdot.png new file mode 100644 index 0000000..056c3f1 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_glowdot.png differ diff --git a/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_handle_pressed.png b/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_handle_pressed.png new file mode 100644 index 0000000..0187a02 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_handle_pressed.png differ diff --git a/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..ba40326 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_glowdot.png b/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_glowdot.png new file mode 100644 index 0000000..cbd039a Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_glowdot.png differ diff --git a/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png b/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png new file mode 100644 index 0000000..2d28009 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png differ diff --git a/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..402ca09 Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/HypoAlarm/src/main/res/drawable/hypoalarm.png b/HypoAlarm/src/main/res/drawable/hypoalarm.png new file mode 100644 index 0000000..70440ec Binary files /dev/null and b/HypoAlarm/src/main/res/drawable/hypoalarm.png differ diff --git a/HypoAlarm/src/main/res/layout/activity_main.xml b/HypoAlarm/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..aa69371 --- /dev/null +++ b/HypoAlarm/src/main/res/layout/activity_main.xml @@ -0,0 +1,7 @@ + diff --git a/HypoAlarm/src/main/res/layout/alarm_alert.xml b/HypoAlarm/src/main/res/layout/alarm_alert.xml new file mode 100644 index 0000000..ae7be7d --- /dev/null +++ b/HypoAlarm/src/main/res/layout/alarm_alert.xml @@ -0,0 +1,37 @@ + + + + + + + + +