diff --git a/HypoAlarm/src/main/AndroidManifest.xml b/HypoAlarm/src/main/AndroidManifest.xml index 906e3b8..9c0752c 100644 --- a/HypoAlarm/src/main/AndroidManifest.xml +++ b/HypoAlarm/src/main/AndroidManifest.xml @@ -21,9 +21,12 @@ android:launchMode="singleInstance" android:noHistory="true"> + + + @@ -35,6 +38,11 @@ android:name=".AlarmNotify" android:label="@string/alarm_notification" > + + + diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java index d3cf51d..0e57744 100644 --- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java @@ -23,8 +23,8 @@ import java.util.Calendar; */ public class AlarmReceiver extends BroadcastReceiver { - private static final int SNOOZE_TIME = 1000*20; //TODO 1000*60*5; // Snooze for 5 minutes if need be - private static final int ALERT_LIFE = 1000*60; //TODO 1000*60*2; // 2 minutes + private static final int SNOOZE_TIME = 0; //1000*60*5; // Snooze for 5 minutes if need be + private static final int ALERT_LIFE = 1000*10; //TODO 1000*60*2; // 2 minutes private static SharedPreferences sharedPref; private static AlarmManager alarmManager, graceManager; private static PendingIntent alarmPendingIntent, gracePendingIntent; @@ -44,10 +44,18 @@ public class AlarmReceiver extends BroadcastReceiver { int gracePeriod = sharedPref.getInt(context.getString(R.string.GracePeriodPref), 60); String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null); + // TODO remove + if (MainActivity.HYPOALARM_DEBUG) { + gracePeriod = 1; + } + if (alarmActive) { // if nothing else happens, assume the alert was ignored. alarmStatus = ALARM_RUNNING; + // Cancel the pre-alarm notification, if it exists + context.stopService(new Intent(context, PreAlarmNotify.class)); + // Set a grace period alarm to send SMS Calendar graceCal = Calendar.getInstance(); graceCal.set(Calendar.SECOND, 0); @@ -76,8 +84,9 @@ public class AlarmReceiver extends BroadcastReceiver { startAlert(context); // Reset for tomorrow; as of API 19, setRepeating() is inexact, so we use setExact() - // (Calendar will automatically advance the day since today's alarmTimeStr is now in the past.) Calendar cal = MainActivity.TimeStringToCalendar(alarmTimeStr); + // Advance the calendar to tomorrow + cal.add(Calendar.DAY_OF_MONTH, 1); alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmPendingIntent = PendingIntent.getBroadcast(context, MainActivity.ALARM_REQUEST, intent, 0); alarmManager.cancel(alarmPendingIntent); @@ -108,8 +117,8 @@ public class AlarmReceiver extends BroadcastReceiver { alarmStatus.contentEquals(ALARM_SNOOZED)) { return; } - // Stop if we're running the snooze alert - if (alarmStatus.contentEquals(ALARM_SNOOZE_RUNNING)) { + // Stop if we're running the snooze alert, or the snooze time is less than 10 seconds + if (alarmStatus.contentEquals(ALARM_SNOOZE_RUNNING) || SNOOZE_TIME < 10000) { stopAlert(context); } else { alarmStatus = ALARM_IGNORED; // This is true, although we are about to switch to ALARM_SNOOZED diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelAlarmReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelAlarmReceiver.java new file mode 100644 index 0000000..ea097e1 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelAlarmReceiver.java @@ -0,0 +1,48 @@ +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.Build; +import android.preference.PreferenceManager; +import android.util.Log; +import android.widget.Toast; + +import java.util.Calendar; + +public class CancelAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null); + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(context, AlarmReceiver.class); + PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(context, MainActivity.ALARM_REQUEST, alarmIntent, 0); + alarmManager.cancel(alarmPendingIntent); + Log.d("CancelAlarmReceiver", "Cancelled grace alarm"); + + // Reset alarm for tomorrow + + // Reset for tomorrow; as of API 19, setRepeating() is inexact, so we use setExact() + Calendar cal = MainActivity.TimeStringToCalendar(alarmTimeStr); + // Set for tomorrow + cal.add(Calendar.DAY_OF_MONTH, 1); + if (Build.VERSION.SDK_INT >= 19) { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmPendingIntent); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmPendingIntent); + } + Log.d("CancelAlarmReceiver", "Resetting alarm for " + MainActivity.debugDate(cal)); + + // Display toast + Toast.makeText(context, context.getString(R.string.alarmCancelToast), Toast.LENGTH_LONG).show(); + + // Remove notification + context.stopService(new Intent(context, PreAlarmNotify.class)); + + } +} \ No newline at end of file diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java index 76696b8..58a7306 100644 --- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/GraceReceiver.java @@ -4,29 +4,64 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; import android.os.PowerManager; import android.preference.PreferenceManager; import android.telephony.SmsManager; import android.util.Log; +import android.widget.Toast; public class GraceReceiver extends BroadcastReceiver { private static SharedPreferences sharedPref; + private static String uri; @Override public void onReceive(Context context, Intent intent) { sharedPref = PreferenceManager.getDefaultSharedPreferences(context); Boolean alarmActive = sharedPref.getBoolean(context.getString(R.string.AlarmActivePref), true); - // TODO get location of phone and send in the message - if (alarmActive) { String phoneNumber = sharedPref.getString(context.getString(R.string.PhoneNumberPref), null); String message = sharedPref.getString(context.getString(R.string.MessagePref), null); + // TODO get location of phone and send in the message + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + LocationListener locationListener = new LocationListener() { + public void onLocationChanged(Location location) { + // Called when a new location is found by the network location provider. + if (location != null) { + double latitude = location.getLatitude(); + double longitude = location.getLongitude(); + + uri = " http://maps.google.com?q=" + location.getLatitude() + "," + location.getLongitude(); + } + } + public void onStatusChanged(String provider, int status, Bundle extras) { + } + public void onProviderEnabled(String provider) { + } + public void onProviderDisabled(String provider) { + } + }; + + if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); + } else if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { + locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener); + } + + message += uri; + SmsManager sms = SmsManager.getDefault(); // TODO uncomment this: //sms.sendTextMessage(phoneNumber, null, message, null, null); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); Log.d("GraceReceiver", "Sending sms to " + phoneNumber); } } + + } diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java index 2c25cea..0097050 100644 --- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/MainActivity.java @@ -62,7 +62,9 @@ import java.util.regex.Pattern; public class MainActivity extends ActionBarActivity { public static int ALARM_REQUEST = 1; public static int GRACE_REQUEST = 2; - public static int CANCEL_GRACE_REQUEST = 3; + public static int PRENOTIFY_REQUEST = 3; + public static int CANCEL_GRACE_REQUEST = 4; + public static int CANCEL_ALARM_REQUEST = 5; private static Switch alarmActiveSwitch; private static Button alarmTimeButton; private static Spinner gracePeriodSpinner; @@ -268,7 +270,7 @@ public class MainActivity extends ActionBarActivity { public static class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener { SharedPreferences sharedPref; private AlarmManager alarmManager; - private PendingIntent alarmPendingIntent; + private PendingIntent alarmPendingIntent, preNotifyPendingIntent; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -319,6 +321,21 @@ public class MainActivity extends ActionBarActivity { } Log.d("MainActivity", "Setting alarm for " + MainActivity.debugDate(cal)); + // Set an alarm for the pre-alarm notification, half an hour before the alarm + Calendar preNotifyCal = cal; + preNotifyCal.add(Calendar.MINUTE, -30); + Intent preNotifyIntent = new Intent(getActivity(), PreAlarmReceiver.class); + preNotifyPendingIntent = PendingIntent.getBroadcast(getActivity(), PRENOTIFY_REQUEST, preNotifyIntent, 0); + // Cancel any existing alarms + alarmManager.cancel(preNotifyPendingIntent); + // Set or reset alarm + if (Build.VERSION.SDK_INT >= 19) { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, preNotifyCal.getTimeInMillis(), preNotifyPendingIntent); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, preNotifyCal.getTimeInMillis(), preNotifyPendingIntent); + } + Log.d("MainActivity", "Setting pre-alarm for " + MainActivity.debugDate(preNotifyCal)); + // Set boot receiver, so alarm restarts on boot ComponentName receiver = new ComponentName(getActivity(), BootReceiver.class); PackageManager pm = getActivity().getPackageManager(); @@ -435,10 +452,6 @@ public class MainActivity extends ActionBarActivity { 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(); @@ -495,6 +508,4 @@ public class MainActivity extends ActionBarActivity { } return remMinutes + minStr; } - - } diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmNotify.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmNotify.java new file mode 100644 index 0000000..a052583 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmNotify.java @@ -0,0 +1,69 @@ +package za.org.treehouse.hypoalarm; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.os.IBinder; +import android.preference.PreferenceManager; +import android.util.Log; + +public class PreAlarmNotify extends Service { + public static final int preNotifyID = 2; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + // Remove the notification in the notification bar + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.cancel(preNotifyID); + Log.d("AlarmNotify", "Pre-notification stopped."); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + + Log.d("PreAlarmNotify", "Pre-notification started."); + + String alarmTimeStr = sharedPref.getString(getString(R.string.AlarmTimePref), null); + + Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_grey); + final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (Build.VERSION.SDK_INT >= 11) { + final Notification.Builder notification = new Notification.Builder(this) + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.preNotificationText)) + .setSmallIcon(R.drawable.alarm_notification) + .setLargeIcon(bm) + .setOnlyAlertOnce(true) + .setAutoCancel(false) + .setPriority(Notification.PRIORITY_HIGH); + + // Set up dismiss action + Intent cancelAlarmIntent = new Intent(getBaseContext(), CancelAlarmReceiver.class); + PendingIntent cancelAlarmPendingIntent = PendingIntent.getBroadcast(getBaseContext(), MainActivity.CANCEL_ALARM_REQUEST, cancelAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + + // Cancel the grace period if the user clears the notification + notification.setDeleteIntent(cancelAlarmPendingIntent); + // Allow the user to cancel by clicking a "Cancel" button + notification.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.preNotificationCancellation), cancelAlarmPendingIntent); + // Allow the user to cancel by selecting the ContentText or ContentTitle + notification.setContentIntent(cancelAlarmPendingIntent); + + nm.cancel(this.preNotifyID); + nm.notify(this.preNotifyID, notification.build()); + } + return super.onStartCommand(intent, flags, startId); + } +} diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmReceiver.java new file mode 100644 index 0000000..107c751 --- /dev/null +++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/PreAlarmReceiver.java @@ -0,0 +1,23 @@ +package za.org.treehouse.hypoalarm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public class PreAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(final Context context, Intent intent) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + Boolean alarmActive = sharedPref.getBoolean(context.getString(R.string.AlarmActivePref), true); + String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null); + + if (alarmActive) { + // Create notification + Intent preNotifyIntent = new Intent(context, PreAlarmNotify.class); + // The PreAlarmNotify service can either be stopped by calling CancelAlarmReceiver, or by waiting for AlarmReceiver to run + context.startService(preNotifyIntent); + } + } +} \ No newline at end of file diff --git a/HypoAlarm/src/main/res/values/strings.xml b/HypoAlarm/src/main/res/values/strings.xml index 0d38b21..aa28b33 100644 --- a/HypoAlarm/src/main/res/values/strings.xml +++ b/HypoAlarm/src/main/res/values/strings.xml @@ -55,6 +55,10 @@ Cancel + Upcoming alarm + + Dismiss Now + All HypoAlarms cancelled