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>
|
1
settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ':HypoAlarm'
|