diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b238d986..49860022 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,7 +24,6 @@ - @@ -48,25 +47,26 @@ + android:parentActivityName=".activities.ControlCenter" /> + android:parentActivityName=".activities.SettingsActivity" /> + android:parentActivityName=".activities.ControlCenter" /> + android:parentActivityName=".activities.SettingsActivity" /> + @@ -87,7 +87,6 @@ - @@ -98,7 +97,6 @@ - @@ -109,7 +107,6 @@ - @@ -123,6 +120,7 @@ + @@ -142,7 +140,6 @@ - @@ -153,7 +150,6 @@ - @@ -164,7 +160,6 @@ - @@ -180,10 +175,11 @@ + + - + android:windowSoftInputMode="stateHidden" /> + android:parentActivityName=".activities.ControlCenter" /> @@ -236,16 +232,31 @@ + android:parentActivityName=".activities.ControlCenter" /> + android:parentActivityName=".activities.SettingsActivity" /> - + android:parentActivityName=".activities.ConfigureAlarms" /> + + + + + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java new file mode 100644 index 00000000..e501de49 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java @@ -0,0 +1,115 @@ +package nodomain.freeyourgadget.gadgetbridge; + +import android.annotation.TargetApi; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.widget.RemoteViews; +import android.widget.Toast; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms; +import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +/** + * Implementation of SleepAlarmWidget functionality. When pressing the widget, an alarm will be set + * to trigger after a predefined number of hours. A toast will confirm the user about this. The + * value is retrieved using ActivityUser.().getActivityUserSleepDuration(). + */ +public class SleepAlarmWidget extends AppWidgetProvider { + + /** + * This is our dedicated action to detect when the widget has been clicked. + */ + public static final String ACTION = + "nodomain.freeyourgadget.gadgetbridge.SLEEP_ALARM_WIDGET_CLICK"; + + static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, + int appWidgetId) { + + // Construct the RemoteViews object + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.sleep_alarm_widget); + + // Add our own click intent + Intent intent = new Intent(ACTION); + PendingIntent clickPI = PendingIntent.getBroadcast( + context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + views.setOnClickPendingIntent(R.id.sleepalarmwidget_text, clickPI); + + // Instruct the widget manager to update the widget + appWidgetManager.updateAppWidget(appWidgetId, views); + } + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // There may be multiple widgets active, so update all of them + for (int appWidgetId : appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId); + } + } + + @Override + public void onEnabled(Context context) { + // Enter relevant functionality for when the first widget is created + } + + @Override + public void onDisabled(Context context) { + // Enter relevant functionality for when the last widget is disabled + } + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + if (ACTION.equals(intent.getAction())) { + int userSleepDuration = new ActivityUser().getActivityUserSleepDuration(); + // current timestamp + GregorianCalendar calendar = new GregorianCalendar(); + // add preferred sleep duration + calendar.add(Calendar.HOUR_OF_DAY, userSleepDuration); + + int hours = calendar.get(calendar.HOUR_OF_DAY); + int minutes = calendar.get(calendar.MINUTE); + + // overwrite the first alarm and activate it + GBAlarm alarm = new GBAlarm(0, true, true, Alarm.ALARM_ONCE, hours, minutes); + alarm.store(); + + if (GBApplication.isRunningLollipopOrLater()) { + setAlarmViaAlarmManager(context, calendar.getTimeInMillis()); + } + + GB.toast(context, + String.format(context.getString(R.string.appwidget_alarms_set), hours, minutes), + Toast.LENGTH_SHORT, GB.INFO); + } + } + + /** + * Use the Android alarm manager to create the alarm icon in the status bar. + * + * @param packageContext {@code Context}: A Context of the application package implementing this + * class. + * @param triggerTime {@code long}: time at which the underlying alarm is triggered in wall time + * milliseconds since the epoch + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void setAlarmViaAlarmManager(Context packageContext, long triggerTime) { + AlarmManager am = (AlarmManager) packageContext.getSystemService(Context.ALARM_SERVICE); + // TODO: launch the alarm configuration activity when clicking the alarm in the status bar + Intent intent = new Intent(packageContext, ConfigureAlarms.class); + PendingIntent pi = PendingIntent.getBroadcast(packageContext, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + am.setAlarmClock(new AlarmManager.AlarmClockInfo(triggerTime, pi), pi); + } +} + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index acf84304..596a5141 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActi import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER; import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM; +import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_SLEEP_DURATION; import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG; import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH; @@ -137,6 +138,7 @@ public class SettingsActivity extends AbstractSettingsActivity { PREF_USER_GENDER, PREF_USER_HEIGHT_CM, PREF_USER_WEIGHT_KG, + PREF_USER_SLEEP_DURATION, }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java index aa4a09b4..cb2b1f48 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -16,17 +16,20 @@ public class ActivityUser { private Integer activityUserYearOfBirth; private Integer activityUserHeightCm; private Integer activityUserWeightKg; + private Integer activityUserSleepDuration; public static final int defaultUserGender = 0; public static final int defaultUserYearOfBirth = 0; public static final int defaultUserAge = 0; public static final int defaultUserHeightCm = 175; public static final int defaultUserWeightKg = 70; + public static final int defaultUserSleepDuration = 7; public static final String PREF_USER_YEAR_OF_BIRTH = "activity_user_year_of_birth"; public static final String PREF_USER_GENDER = "activity_user_gender"; public static final String PREF_USER_HEIGHT_CM = "activity_user_height_cm"; public static final String PREF_USER_WEIGHT_KG = "activity_user_weight_kg"; + public static final String PREF_USER_SLEEP_DURATION = "activity_user_sleep_duration"; public int getActivityUserWeightKg() { if (activityUserWeightKg == null) { @@ -56,6 +59,20 @@ public class ActivityUser { return activityUserHeightCm; } + /** + * @return the user defined sleep duration or the default value when none is set or the stored + * value is out of any logical bounds. + */ + public int getActivityUserSleepDuration() { + if(activityUserSleepDuration == null) { + fetchPreferences(); + } + if (activityUserSleepDuration < 1 || activityUserSleepDuration > 24) { + activityUserSleepDuration = defaultUserSleepDuration; + } + return activityUserSleepDuration; + } + public int getActivityUserAge() { int userYear = getActivityUserYearOfBirth(); int age = 25; @@ -74,5 +91,6 @@ public class ActivityUser { activityUserHeightCm = Integer.parseInt(prefs.getString(PREF_USER_HEIGHT_CM, Integer.toString(defaultUserHeightCm))); activityUserWeightKg = Integer.parseInt(prefs.getString(PREF_USER_WEIGHT_KG, Integer.toString(defaultUserWeightKg))); activityUserYearOfBirth = Integer.parseInt(prefs.getString(PREF_USER_YEAR_OF_BIRTH, Integer.toString(defaultUserYearOfBirth))); + activityUserSleepDuration = Integer.parseInt(prefs.getString(PREF_USER_SLEEP_DURATION, Integer.toString(defaultUserSleepDuration))); } } diff --git a/app/src/main/res/layout/sleep_alarm_widget.xml b/app/src/main/res/layout/sleep_alarm_widget.xml new file mode 100644 index 00000000..b1b9c8c9 --- /dev/null +++ b/app/src/main/res/layout/sleep_alarm_widget.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8426034e..757aa96c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -200,4 +200,6 @@ Diese Firmware ist nicht mit dem Gerät kompatibel warte auf eingehende Verbindung Erneut installieren + Widget hinzufügen + Bevorzugte Schlafdauer in Stunden diff --git a/app/src/main/res/values-v14/dimens.xml b/app/src/main/res/values-v14/dimens.xml new file mode 100644 index 00000000..4db8c590 --- /dev/null +++ b/app/src/main/res/values-v14/dimens.xml @@ -0,0 +1,10 @@ + + + + + 0dp + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 47c82246..fb6e2350 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,4 +2,10 @@ 16dp 16dp + + + 8dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14951692..8ab85988 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -225,4 +225,9 @@ authenticating authentication required + Zzz + Add widget + Preferred sleep duration in hours + An alarm was set for %1$02d:%2$02d + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index cb2a8a6f..60eb7c5f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -153,6 +153,12 @@ android:key="activity_user_weight_kg" android:maxLength="3" android:title="@string/activity_prefs_weight_kg" /> + + + \ No newline at end of file diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 510095e5..77158b15 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -10,7 +10,6 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;