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