Commit some new files.
							
								
								
									
										77
									
								
								HypoAlarm/HypoAlarm-HypoAlarm.iml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,77 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="HypoAlarm" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> | ||||
|   <component name="FacetManager"> | ||||
|     <facet type="android-gradle" name="Android-Gradle"> | ||||
|       <configuration> | ||||
|         <option name="GRADLE_PROJECT_PATH" value=":HypoAlarm" /> | ||||
|       </configuration> | ||||
|     </facet> | ||||
|     <facet type="android" name="Android"> | ||||
|       <configuration> | ||||
|         <option name="SELECTED_BUILD_VARIANT" value="debug" /> | ||||
|         <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> | ||||
|         <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugJava" /> | ||||
|         <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" /> | ||||
|         <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" /> | ||||
|         <option name="ALLOW_USER_CONFIGURATION" value="false" /> | ||||
|         <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> | ||||
|         <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" /> | ||||
|         <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" /> | ||||
|         <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> | ||||
|       </configuration> | ||||
|     </facet> | ||||
|   </component> | ||||
|   <component name="NewModuleRootManager" inherit-compiler-output="false"> | ||||
|     <output url="file://$MODULE_DIR$/build/classes/debug" /> | ||||
|     <exclude-output /> | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/r/debug" isTestSource="false" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/aidl/debug" isTestSource="false" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/debug" isTestSource="false" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/rs/debug" isTestSource="false" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/res/rs/debug" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/r/test/debug" isTestSource="true" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/aidl/test/debug" isTestSource="true" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/test/debug" isTestSource="true" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/source/rs/test/debug" isTestSource="true" generated="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/build/res/rs/test/debug" type="java-test-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" isTestSource="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/apk" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/assets" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/bundles" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/classes" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/incremental" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/libs" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/manifests" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/res" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/symbols" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/build/tmp" /> | ||||
|     </content> | ||||
|     <orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|     <orderEntry type="library" exported="" name="appcompat-v7-19.0.1" level="project" /> | ||||
|     <orderEntry type="library" exported="" name="support-v4-19.0.1" level="project" /> | ||||
|   </component> | ||||
| </module> | ||||
|  | ||||
							
								
								
									
										25
									
								
								HypoAlarm/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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']) | ||||
| } | ||||
							
								
								
									
										17
									
								
								HypoAlarm/proguard-rules.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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 *; | ||||
| #} | ||||
							
								
								
									
										52
									
								
								HypoAlarm/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,52 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="za.org.treehouse.hypoalarm" > | ||||
|  | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@drawable/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:theme="@style/AppTheme" > | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:label="@string/app_name" > | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|                 <activity | ||||
|                     android:name=".AlarmAlertActivity" | ||||
|                     android:label="@string/alarm_alert" | ||||
|                     android:launchMode="singleInstance" | ||||
|                     android:noHistory="true"> | ||||
|         </activity> | ||||
|         <receiver android:name=".AlarmReceiver"/> | ||||
|         <receiver android:name=".GraceReceiver"/> | ||||
|         <receiver android:name=".CancelGraceReceiver"/> | ||||
|         <receiver android:name=".BootReceiver" | ||||
|             android:enabled="false" > | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.BOOT_COMPLETED"/> | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|         <service | ||||
|             android:name=".AlarmNotify" | ||||
|             android:label="@string/alarm_notification" > | ||||
|         </service> | ||||
|     </application> | ||||
|  | ||||
|     <!-- permission required to read contacts --> | ||||
|     <uses-permission android:name="android.permission.READ_CONTACTS"/> | ||||
|     <!-- permission required to set an alarm --> | ||||
|     <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/> | ||||
|     <!-- permission required to Send SMS --> | ||||
|     <uses-permission android:name="android.permission.SEND_SMS"/> | ||||
|     <!-- permission to restart the alarm on device boot --> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> | ||||
|     <!-- permission required to vibrate --> | ||||
|     <uses-permission android:name="android.permission.VIBRATE"/> | ||||
|     <!-- permission required to wake the device --> | ||||
|     <uses-permission android:name="android.permission.WAKE_LOCK"/> | ||||
|  | ||||
| </manifest> | ||||
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/ic_launcher-web.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 116 KiB | 
| @@ -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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-hdpi/alarm_notification.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-hdpi/ic_lockscreen_glowdot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 738 B | 
| After Width: | Height: | Size: 8.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-mdpi/alarm_notification.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-mdpi/ic_lockscreen_glowdot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 538 B | 
| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-xhdpi/ic_lockscreen_glowdot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 964 B | 
| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								HypoAlarm/src/main/res/drawable/hypoalarm.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 34 KiB | 
							
								
								
									
										7
									
								
								HypoAlarm/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/container" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     tools:context="za.org.treehouse.remotediabetesalarm.MainActivity" | ||||
|     tools:ignore="MergeRootFrame" /> | ||||
							
								
								
									
										37
									
								
								HypoAlarm/src/main/res/layout/alarm_alert.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
|  | ||||
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" android:layout_height="match_parent" | ||||
|     tools:context=".AlarmNotificationActivity"> | ||||
|  | ||||
|     <ImageView | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:id="@+id/cancel_dialog_imageView" | ||||
|         android:src="@drawable/hypoalarm" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:layout_centerHorizontal="true" | ||||
|         android:layout_marginTop="60dp"/> | ||||
|  | ||||
|     <TextView | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|         android:text="HypoAlarm!" | ||||
|         android:id="@+id/cancel_dialog_title" | ||||
|         android:layout_below="@+id/cancel_dialog_imageView" | ||||
|         android:layout_centerHorizontal="true" | ||||
|         android:layout_marginTop="25dp"  /> | ||||
|  | ||||
|     <Button | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:text="I'm awake" | ||||
|         android:id="@+id/cancel_dialog_button" | ||||
|         android:layout_marginTop="25dp" | ||||
|         android:layout_below="@+id/cancel_dialog_title" | ||||
|         android:layout_centerHorizontal="true" /> | ||||
|  | ||||
| </RelativeLayout> | ||||
							
								
								
									
										138
									
								
								HypoAlarm/src/main/res/layout/fragment_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,138 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
|  | ||||
| <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="fill_parent" | ||||
|     android:layout_height="fill_parent" | ||||
|     android:stretchColumns="0" | ||||
|     android:shrinkColumns="1" | ||||
|     android:dividerPadding="10dp" | ||||
|     android:paddingLeft="@dimen/activity_horizontal_margin" | ||||
|     android:paddingRight="@dimen/activity_horizontal_margin" | ||||
|     android:paddingTop="@dimen/activity_vertical_margin" | ||||
|     android:paddingBottom="@dimen/activity_vertical_margin"> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|  | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/description" | ||||
|             android:text="@string/description" | ||||
|             android:layout_span="2" | ||||
|             android:layout_column="0" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|  | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:text="@string/alarm_active_text" | ||||
|             android:id="@+id/alarm_active_text" | ||||
|             android:layout_column="0" /> | ||||
|  | ||||
|         <Switch | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/alarm_active_switch" | ||||
|             android:layout_column="1" | ||||
|             android:layout_gravity="left" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:text="@string/alarm_time_text" | ||||
|             android:id="@+id/alarm_time_text" | ||||
|             android:layout_column="0" /> | ||||
|  | ||||
|  | ||||
|         <Button | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/alarm_time" | ||||
|             android:gravity="center_vertical" | ||||
|             style="?android:attr/borderlessButtonStyle" | ||||
|             android:layout_column="1" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:text="@string/grace_period_text" | ||||
|             android:id="@+id/grace_period_text" | ||||
|             android:layout_column="0" /> | ||||
|  | ||||
|         <Spinner | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/grace_period" | ||||
|             android:entries="@array/grace_period_array" | ||||
|             android:gravity="left" | ||||
|             android:layout_column="1" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:text="@string/phone_number_text" | ||||
|             android:id="@+id/phone_number_text" | ||||
|             android:layout_column="0" /> | ||||
|  | ||||
|         <EditText | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/phone_number" | ||||
|             android:inputType="phone" | ||||
|             android:focusable="false" | ||||
|             android:focusableInTouchMode="false" | ||||
|             android:clickable="true" | ||||
|             android:gravity="left" | ||||
|             android:layout_column="1" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|         <TextView | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:text="@string/message_text" | ||||
|             android:id="@+id/message_text" | ||||
|             android:layout_span="2" | ||||
|             android:layout_column="0" /> | ||||
|     </TableRow> | ||||
|  | ||||
|     <TableRow | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="fill_parent"> | ||||
|         <EditText | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:id="@+id/message" | ||||
|             android:focusable="false" | ||||
|             android:focusableInTouchMode="false" | ||||
|             android:clickable="true" | ||||
|             android:layout_span="2" | ||||
|             android:layout_column="0" /> | ||||
|     </TableRow> | ||||
| </TableLayout> | ||||
							
								
								
									
										25
									
								
								HypoAlarm/src/main/res/layout/phone_dialog.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
|  | ||||
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" android:layout_height="match_parent"> | ||||
|  | ||||
|     <EditText | ||||
|         android:layout_width="fill_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:inputType="phone" | ||||
|         android:ems="10" | ||||
|         android:id="@+id/dialog_phone_number" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:layout_toLeftOf="@+id/dialog_contacts_button" /> | ||||
|  | ||||
|     <Button | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:id="@+id/dialog_contacts_button" | ||||
|         android:text="@string/dialog_contacts" | ||||
|         android:gravity="center" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:layout_alignParentRight="true" | ||||
|         android:textSize="14sp" /> | ||||
|  | ||||
| </RelativeLayout> | ||||
							
								
								
									
										6
									
								
								HypoAlarm/src/main/res/values-w820dp/dimens.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| <resources> | ||||
|     <!-- Example customization of dimensions originally defined in res/values/dimens.xml | ||||
|          (such as screen margins) for screens with more than 820dp of available width. This | ||||
|          would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> | ||||
|     <dimen name="activity_horizontal_margin">64dp</dimen> | ||||
| </resources> | ||||
							
								
								
									
										2
									
								
								HypoAlarm/src/main/res/values-w820dp/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources></resources> | ||||
							
								
								
									
										30
									
								
								HypoAlarm/src/main/res/values/dimens.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- Default screen margins, per the Android Design guidelines. --> | ||||
|     <dimen name="activity_horizontal_margin">16dp</dimen> | ||||
|     <dimen name="activity_vertical_margin">16dp</dimen> | ||||
|  | ||||
|     <!-- Default target placement radius for GlowPadView. Should be 1/2 of outerring diameter. --> | ||||
|     <dimen name="glowpadview_target_placement_radius">135dip</dimen> | ||||
|  | ||||
|     <!-- Default glow radius for GlowPadView --> | ||||
|     <dimen name="glowpadview_glow_radius">75dip</dimen> | ||||
|  | ||||
|     <!-- Default distance beyond which GlowPadView snaps to the matching target --> | ||||
|     <dimen name="glowpadview_snap_margin">40dip</dimen> | ||||
|  | ||||
|     <!-- Default distance from each snap target that GlowPadView considers a "hit" --> | ||||
|     <dimen name="glowpadview_inner_radius">15dip</dimen> | ||||
|  | ||||
|     <!-- Size of lockscreen outerring on unsecure unlock LockScreen --> | ||||
|     <dimen name="keyguard_lockscreen_outerring_diameter">270dp</dimen> | ||||
|  | ||||
|     <!-- Circle size for incoming call widget's each item. --> | ||||
|     <dimen name="incoming_call_widget_circle_size">94dp</dimen> | ||||
|  | ||||
|     <!-- Margin used for incoming call widget's icon for each item. | ||||
|     This should be same as "(incoming_call_widget_circle_size - icon_size)/2". | ||||
|     Right now answer/decline/reject icons have 38dp width/height. | ||||
|     So, (94 - 38)/2 ==> 28dp --> | ||||
|     <dimen name="incoming_call_widget_asset_margin">28dp</dimen> | ||||
| </resources> | ||||
							
								
								
									
										60
									
								
								HypoAlarm/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|  | ||||
|     <string name="app_name">HypoAlarm</string> | ||||
|  | ||||
|     <string name="alarm_alert">Alarm Alert</string> | ||||
|  | ||||
|     <string name="alarm_notification">SMS pending</string> | ||||
|  | ||||
|     <string-array name="grace_period_array"> | ||||
|         <item>10 minutes</item> | ||||
|         <item>15 minutes</item> | ||||
|         <item>30 minutes</item> | ||||
|         <item>45 minutes</item> | ||||
|         <item>1 hour</item> | ||||
|         <item>1 hour 30 minutes</item> | ||||
|         <item>2 hours</item> | ||||
|     </string-array> | ||||
|  | ||||
|     <string name="description">Set an alarm here. When the alarm goes off every day, you\'ll have a period of grace to reset it. After that, your phone will SMS the specified phone number with an alert.</string> | ||||
|  | ||||
|     <string name="alarm_active_text">Alarm active</string> | ||||
|  | ||||
|     <string name="alarm_time_text">Alarm time</string> | ||||
|  | ||||
|     <string name="grace_period_text">Grace period</string> | ||||
|  | ||||
|     <string name="phone_number_text">Phone number</string> | ||||
|  | ||||
|     <string name="message_text">Message</string> | ||||
|  | ||||
|     <string name="AlarmActivePref">AlarmActive</string> | ||||
|  | ||||
|     <string name="AlarmTimePref">AlarmTime</string> | ||||
|  | ||||
|     <string name="GracePeriodPref">GracePeriod</string> | ||||
|  | ||||
|     <string name="PhoneNumberPref">PhoneNumber</string> | ||||
|  | ||||
|     <string name="MessagePref">Message</string> | ||||
|  | ||||
|     <string name="setPhoneNumber">Set phone number to alert</string> | ||||
|  | ||||
|     <string name="setMessage">Message text</string> | ||||
|  | ||||
|     <string name="dialog_contacts">Contacts</string> | ||||
|  | ||||
|     <string name="alarmSetToast">HypoAlarm set to </string> | ||||
|  | ||||
|     <string name="alarmCancelToast">HypoAlarm SMS cancelled</string> | ||||
|  | ||||
|     <string name="defaultMessage">Hi, I haven\'t responded to my alarm today. Please contact me to make sure I\'m awake.</string> | ||||
|  | ||||
|     <string name="notificationText">An SMS will be sent in %1$s</string> | ||||
|  | ||||
|     <string name="notificationCancellation">Cancel</string> | ||||
|  | ||||
|     <string name="alarmCancelled">All HypoAlarms cancelled</string> | ||||
|  | ||||
| </resources> | ||||
							
								
								
									
										8
									
								
								HypoAlarm/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | ||||
| <resources> | ||||
|  | ||||
|     <!-- Base application theme. --> | ||||
|     <style name="AppTheme" parent="Theme.AppCompat"> | ||||
|         <!-- Customize your theme here. --> | ||||
|     </style> | ||||
|  | ||||
| </resources> | ||||