diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b238d9869..49860022e 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 000000000..e501de497
--- /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 acf843044..596a5141b 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 aa4a09b4c..cb2b1f489 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 000000000..b1b9c8c92
--- /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 8426034e8..757aa96c9 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 000000000..4db8c5906
--- /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 47c822467..fb6e23509 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 149516920..8ab859887 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 cb2a8a6fb..60eb7c5fc 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 510095e5a..77158b155 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;