1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-24 02:46:50 +01:00

Add Status and Alarms widget

Squashed commits from #1604
This commit is contained in:
vanous 2019-08-13 19:54:18 +02:00 committed by Andreas Shimokawa
parent b40c3ade85
commit 957d441859
11 changed files with 792 additions and 7 deletions

View File

@ -445,7 +445,8 @@
android:resource="@xml/shared_paths" />
</provider>
<receiver android:name=".SleepAlarmWidget">
<receiver android:name=".SleepAlarmWidget"
android:label="@string/appwidget_sleep_alarm_widget_label">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="nodomain.freeyourgadget.gadgetbridge.SLEEP_ALARM_WIDGET_CLICK" />
@ -456,6 +457,26 @@
android:resource="@xml/sleep_alarm_widget_info" />
</receiver>
<receiver
android:name=".Widget"
android:label="@string/widget_listing_label">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="nodomain.freeyourgadget.gadgetbridge.WidgetClick" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
<activity
android:name=".activities.WidgetAlarmsActivity"
android:launchMode="singleInstance"
android:theme="@style/Theme.AppCompat.Light.Dialog"
android:excludeFromRecents="true"/>
<activity
android:launchMode="singleTask"
android:allowTaskReparenting="true"

View File

@ -0,0 +1,226 @@
/* Copyright (C) 2016-2019 0nse, Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.RemoteViews;
import android.widget.Toast;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.WidgetAlarmsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class Widget extends AppWidgetProvider {
public static final String WIDGET_CLICK = "nodomain.freeyourgadget.gadgetbridge.WidgetClick";
public static final String NEW_DATA_ACTION = "nodomain.freeyourgadget.gadgetbridge.NewDataTrigger";
public static final String APPWIDGET_DELETED = "nodomain.freeyourgadget.gadgetbridge.APPWIDGET_DELETED";
public static final String ACTION_DEVICE_CHANGED = "nodomain.freeyourgadget.gadgetbridge.gbdevice.action.device_changed";
private static final Logger LOG = LoggerFactory.getLogger(Widget.class);
static BroadcastReceiver broadcastReceiver = null;
private GBDevice getSelectedDevice() {
Context context = GBApplication.getContext();
if (!(context instanceof GBApplication)) {
return null;
}
GBApplication gbApp = (GBApplication) context;
return gbApp.getDeviceManager().getSelectedDevice();
}
private float[] getSteps() {
Context context = GBApplication.getContext();
Calendar day = GregorianCalendar.getInstance();
if (!(context instanceof GBApplication)) {
return new float[]{0, 0, 0};
}
DailyTotals ds = new DailyTotals();
return ds.getDailyTotalsForAllDevices(day);
}
private String getHM(long value) {
return DateTimeUtils.formatDurationHoursMinutes(value, TimeUnit.MINUTES);
}
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
GBDevice device = getSelectedDevice();
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
//onclick refresh
Intent intent = new Intent(context, Widget.class);
intent.setAction(WIDGET_CLICK);
PendingIntent refreshDataIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//views.setOnClickPendingIntent(R.id.todaywidget_bottom_layout, refreshDataIntent);
views.setOnClickPendingIntent(R.id.todaywidget_header_bar, refreshDataIntent);
//open GB main window
Intent startMainIntent = new Intent(context, ControlCenterv2.class);
PendingIntent startMainPIntent = PendingIntent.getActivity(context, 0, startMainIntent, 0);
views.setOnClickPendingIntent(R.id.todaywidget_header_icon, startMainPIntent);
//alarms popup menu
Intent startAlarmListIntent = new Intent(context, WidgetAlarmsActivity.class);
PendingIntent startAlarmListPIntent = PendingIntent.getActivity(context, 0, startAlarmListIntent, 0);
views.setOnClickPendingIntent(R.id.todaywidget_header_plus, startAlarmListPIntent);
//charts, requires device
if (device != null) {
Intent startChartsIntent = new Intent(context, ChartsActivity.class);
startChartsIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
PendingIntent startChartsPIntent = PendingIntent.getActivity(context, 0, startChartsIntent, 0);
views.setOnClickPendingIntent(R.id.todaywidget_bottom_layout, startChartsPIntent);
}
float[] DailyTotals = getSteps();
views.setTextViewText(R.id.todaywidget_steps, context.getString(R.string.widget_steps_label, (int) DailyTotals[0]));
views.setTextViewText(R.id.todaywidget_sleep, context.getString(R.string.widget_sleep_label, getHM((long) DailyTotals[1])));
if (device != null) {
String status = String.format("%1s", device.getStateString());
if (device.isConnected()) {
if (device.getBatteryLevel() > 1) {
status = String.format("Battery %1s%%", device.getBatteryLevel());
}
}
views.setTextViewText(R.id.todaywidget_device_status, status);
views.setTextViewText(R.id.todaywidget_device_name, device.getName());
}
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
public void refreshData() {
Context context = GBApplication.getContext();
GBDevice device = getSelectedDevice();
if (device == null || !device.isInitialized()) {
GB.toast(context,
context.getString(R.string.device_not_connected),
Toast.LENGTH_SHORT, GB.ERROR);
GBApplication.deviceService().connect();
GB.toast(context,
context.getString(R.string.connecting),
Toast.LENGTH_SHORT, GB.INFO);
return;
}
GB.toast(context,
context.getString(R.string.busy_task_fetch_activity_data),
Toast.LENGTH_SHORT, GB.INFO);
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
}
public void updateWidget() {
Context context = GBApplication.getContext();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), Widget.class.getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
onUpdate(context, appWidgetManager, appWidgetIds);
}
@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) {
if (this.broadcastReceiver == null) {
LOG.debug("gbwidget BROADCAST receiver initialized.");
this.broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
LOG.debug("gbwidget BROADCAST, action" + intent.getAction());
updateWidget();
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(NEW_DATA_ACTION);
intentFilter.addAction(ACTION_DEVICE_CHANGED);
LocalBroadcastManager.getInstance(context).registerReceiver(this.broadcastReceiver, intentFilter);
}
}
@Override
public void onDisabled(Context context) {
if (this.broadcastReceiver != null) {
LocalBroadcastManager.getInstance(context).unregisterReceiver(this.broadcastReceiver);
this.broadcastReceiver=null;
}
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
LOG.debug("gbwidget LOCAL onReceive, action: " + intent.getAction());
//this handles widget re-connection after apk updates
if (WIDGET_CLICK.equals(intent.getAction())) {
if (this.broadcastReceiver == null) {
onEnabled(context);
}
refreshData();
//updateWidget();
} else if (APPWIDGET_DELETED.equals(intent.getAction())) {
onDisabled(context);
}
}
}

View File

@ -0,0 +1,127 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class WidgetAlarmsActivity extends Activity implements View.OnClickListener {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context appContext = this.getApplicationContext();
if (appContext instanceof GBApplication) {
GBApplication gbApp = (GBApplication) appContext;
GBDevice selectedDevice = gbApp.getDeviceManager().getSelectedDevice();
if (selectedDevice == null || !selectedDevice.isInitialized()) {
GB.toast(this,
this.getString(R.string.not_connected),
Toast.LENGTH_LONG, GB.WARN);
} else {
setContentView(R.layout.widget_alarms_activity_list);
int userSleepDuration = new ActivityUser().getSleepDuration();
textView = findViewById(R.id.alarm5);
if (userSleepDuration > 0) {
textView.setText(String.format(this.getString(R.string.widget_alarm_target_hours), userSleepDuration));
} else {
textView.setVisibility(View.GONE);
}
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.alarm1:
setAlarm(5);
break;
case R.id.alarm2:
setAlarm(10);
break;
case R.id.alarm3:
setAlarm(20);
break;
case R.id.alarm4:
setAlarm(60);
break;
case R.id.alarm5:
setAlarm(0);
break;
default:
break;
}
//this is to prevent screen flashing during closing
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 150);
}
public void setAlarm(int duration) {
// current timestamp
GregorianCalendar calendar = new GregorianCalendar();
if (duration > 0) {
calendar.add(Calendar.MINUTE, duration);
} else {
int userSleepDuration = new ActivityUser().getSleepDuration();
// add preferred sleep duration
if (userSleepDuration > 0) {
calendar.add(Calendar.HOUR_OF_DAY, userSleepDuration);
} else { // probably testing
calendar.add(Calendar.MINUTE, 1);
}
}
// overwrite the first alarm and activate it, without
Context appContext = this.getApplicationContext();
if (appContext instanceof GBApplication) {
GBApplication gbApp = (GBApplication) appContext;
GBDevice selectedDevice = gbApp.getDeviceManager().getSelectedDevice();
if (selectedDevice == null || !selectedDevice.isInitialized()) {
GB.toast(this,
this.getString(R.string.appwidget_not_connected),
Toast.LENGTH_LONG, GB.WARN);
return;
}
}
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
GB.toast(this,
this.getString(R.string.appwidget_setting_alarm, hours, minutes),
Toast.LENGTH_SHORT, GB.INFO);
Alarm alarm = AlarmUtils.createSingleShot(0, true, calendar);
ArrayList<Alarm> alarms = new ArrayList<>(1);
alarms.add(alarm);
GBApplication.deviceService().onSetAlarms(alarms);
}
}

View File

@ -28,15 +28,15 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
class ActivityAnalysis {
private static final Logger LOG = LoggerFactory.getLogger(ActivityAnalysis.class);
public class ActivityAnalysis {
public static final Logger LOG = LoggerFactory.getLogger(ActivityAnalysis.class);
// store raw steps and duration
protected HashMap<Integer, Long> stats = new HashMap<Integer, Long>();
// max speed determined from samples
private int maxSpeed = 0;
ActivityAmounts calculateActivityAmounts(List<? extends ActivitySample> samples) {
public ActivityAmounts calculateActivityAmounts(List<? extends ActivitySample> samples) {
ActivityAmount deepSleep = new ActivityAmount(ActivityKind.TYPE_DEEP_SLEEP);
ActivityAmount lightSleep = new ActivityAmount(ActivityKind.TYPE_LIGHT_SLEEP);
ActivityAmount notWorn = new ActivityAmount(ActivityKind.TYPE_NOT_WORN);

View File

@ -0,0 +1,155 @@
/* Copyright (C) 2017-2019 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.model;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityAnalysis;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class DailyTotals {
Logger LOG = LoggerFactory.getLogger(DailyTotals.class);
public float[] getDailyTotalsForAllDevices(Calendar day) {
Context context = GBApplication.getContext();
//get today's steps for all devices in GB
float all_steps = 0;
float all_sleep = 0;
if (context instanceof GBApplication) {
GBApplication gbApp = (GBApplication) context;
List<? extends GBDevice> devices = gbApp.getDeviceManager().getDevices();
for (GBDevice device : devices) {
float[] all_daily = getDailyTotalsForDevice(device, day);
all_steps += all_daily[0];
all_sleep += all_daily[1] + all_daily[2];
}
}
LOG.debug("gbwidget daily totals, all steps:" + all_steps);
LOG.debug("gbwidget daily totals, all sleep:" + all_sleep);
return new float[]{all_steps, all_sleep};
}
public float[] getDailyTotalsForDevice(GBDevice device, Calendar day
) {
try (DBHandler handler = GBApplication.acquireDB()) {
ActivityAnalysis analysis = new ActivityAnalysis();
ActivityAmounts amountsSteps = null;
ActivityAmounts amountsSleep = null;
amountsSteps = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, 0, device));
amountsSleep = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, -12, device));
float[] Sleep = getTotalsSleepForActivityAmounts(amountsSleep);
float Steps = getTotalsStepsForActivityAmounts(amountsSteps);
return new float[]{Steps, Sleep[0], Sleep[1]};
} catch (Exception e) {
GB.toast("Error loading activity summaries.", Toast.LENGTH_SHORT, GB.ERROR, e);
return new float[]{0, 0, 0};
}
}
private float[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {
long totalSecondsDeepSleep = 0;
long totalSecondsLightSleep = 0;
for (ActivityAmount amount : activityAmounts.getAmounts()) {
if (amount.getActivityKind() == ActivityKind.TYPE_DEEP_SLEEP) {
totalSecondsDeepSleep += amount.getTotalSeconds();
} else if (amount.getActivityKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
totalSecondsLightSleep += amount.getTotalSeconds();
}
}
int totalMinutesDeepSleep = (int) (totalSecondsDeepSleep / 60);
int totalMinutesLightSleep = (int) (totalSecondsLightSleep / 60);
return new float[]{totalMinutesDeepSleep, totalMinutesLightSleep};
}
private float getTotalsStepsForActivityAmounts(ActivityAmounts activityAmounts) {
long totalSteps = 0;
float totalValue = 0;
for (ActivityAmount amount : activityAmounts.getAmounts()) {
totalSteps += amount.getTotalSteps();
}
float[] totalValues = new float[]{totalSteps};
for (int i = 0; i < totalValues.length; i++) {
float value = totalValues[i];
totalValue += value;
}
return totalValue;
}
private List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
int startTs;
int endTs;
day = (Calendar) day.clone(); // do not modify the caller's argument
day.set(Calendar.HOUR_OF_DAY, 0);
day.set(Calendar.MINUTE, 0);
day.set(Calendar.SECOND, 0);
day.add(Calendar.HOUR, offsetHours);
startTs = (int) (day.getTimeInMillis() / 1000);
endTs = startTs + 24 * 60 * 60 - 1;
return getSamples(db, device, startTs, endTs);
}
protected List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
return getAllSamples(db, device, tsFrom, tsTo);
}
protected SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
return coordinator.getSampleProvider(device, db.getDaoSession());
}
protected List<? extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
SampleProvider<? extends ActivitySample> provider = getProvider(db, device);
return provider.getAllActivitySamples(tsFrom, tsTo);
}
}

View File

@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Intent;
import android.content.SharedPreferences;
import android.widget.Toast;
@ -126,6 +127,8 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation {
GB.updateTransferNotification(null, "", false, 100, getContext());
operationFinished();
unsetBusy();
Intent intent = new Intent("nodomain.freeyourgadget.gadgetbridge.NewDataTrigger");
getContext().sendBroadcast(intent);
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,121 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/alarm_list_top_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary_dark"
android:orientation="vertical">
<LinearLayout
android:id="@+id/todaywidget_header_container"
android:layout_width="match_parent"
android:layout_height="34dp"
android:background="@color/accent"
android:orientation="horizontal">
<ImageView
android:id="@+id/todaywidget_header_icon"
android:layout_width="32dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:adjustViewBounds="false"
android:contentDescription="@string/icon_placeholder"
android:cropToPadding="false"
android:padding="0dp"
android:src="@drawable/ic_notification" />
<LinearLayout
android:id="@+id/todaywidget_header_bar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<TextView
android:id="@+id/todaywidget_device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:drawablePadding="4dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/primarytext_dark"
android:textSize="18sp" />
</LinearLayout>
<ImageView
android:id="@+id/todaywidget_header_plus"
android:layout_width="32dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:contentDescription="@string/icon_placeholder"
android:padding="0dp"
android:src="@drawable/ic_watchface" />
</LinearLayout>
<LinearLayout
android:id="@+id/todaywidget_bottom_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="3dp"
android:paddingRight="3dp">
<LinearLayout
android:id="@+id/todaywidget_bottom_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/todaywidget_steps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/widget_steps_label"
android:gravity="center_vertical|center_horizontal"
android:text="@string/widget_steps_label"
android:textColor="@color/primarytext_dark"
android:textSize="18sp" />
<TextView
android:id="@+id/todaywidget_sleep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/widget_sleep_label"
android:gravity="center_vertical|center_horizontal"
android:text="@string/widget_sleep_label"
android:textColor="@color/primarytext_dark"
android:textSize="18sp" />
</LinearLayout>
<TextView
android:id="@+id/empty"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/todaywidget_device_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:foregroundGravity="right"
android:gravity="end"
android:text="@string/device_not_connected"
android:textColor="@color/primarytext_dark"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/alarm_list_top_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/primary_dark"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/todaywidget_header_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/accent"
android:baselineAligned="false"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/todaywidget_header_bar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<TextView
android:id="@+id/todaywidget_alarm_list_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:drawablePadding="4dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/widget_set_alarm_after"
android:textColor="@color/primarytext_dark"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/todaywidget_alarm_list_bottom_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<TextView
android:id="@+id/alarm1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/widget_5_minutes"
android:textSize="30sp" />
<TextView
android:id="@+id/alarm2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/widget_10_minutes"
android:textSize="30sp" />
<TextView
android:id="@+id/alarm3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/widget_20_minutes"
android:textSize="30sp" />
<TextView
android:id="@+id/alarm4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/widget_1_hour"
android:textSize="30sp" />
<TextView
android:id="@+id/alarm5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/miband_prefs_fitness_goal"
android:textSize="30sp" />
</LinearLayout>
</LinearLayout>

View File

@ -527,10 +527,7 @@
<string name="authenticating">Authenticating</string>
<string name="authentication_required">Authentication required</string>
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Add widget</string>
<string name="activity_prefs_sleep_duration">Preferred sleep duration in hours</string>
<string name="appwidget_setting_alarm">Setting alarm for %1$02d:%2$02d</string>
<string name="device_hw">Hardware revision: %1$s</string>
<string name="device_fw">Firmware version: %1$s</string>
<string name="error_creating_directory_for_logfiles">Error creating directory for log files: %1$s</string>
@ -715,5 +712,23 @@
<string name="filter_mode">Filter Mode</string>
<string name="mode_configuration">Mode Configuration</string>
<string name="save_configuration">Save Configuration</string>
<!-- App Widgets-->
<string name="appwidget_not_connected">Not connected, alarm not set.</string>
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Add widget</string>
<string name="appwidget_setting_alarm">Setting alarm for %1$02d:%2$02d</string>
<string name="appwidget_sleep_alarm_widget_label">Sleep Alarm</string>
<string name="widget_steps_label">Steps: %1$02d</string>
<string name="widget_sleep_label">Sleep: %1$s</string>
<string name="widget_listing_label">Status and Alarms</string>
<string name="widget_set_alarm_after">Set alarm after:</string>
<string name="widget_5_minutes">5 minutes</string>
<string name="widget_10_minutes">10 minutes</string>
<string name="widget_20_minutes">20 minutes</string>
<string name="widget_1_hour">1 hour</string>
<string name="icon_placeholder" translatable="false">Icon</string>
<string name="widget_alarm_target_hours">%d hours</string>
</resources>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/widget"
android:initialLayout="@layout/widget"
android:minHeight="50dp"
android:minWidth="100dp"
minResizeHeight="5dp"
minResizeWidth="5dp"
android:resizeMode="horizontal|vertical"
android:previewImage="@drawable/widget_preview"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen|keyguard|searchbox">
</appwidget-provider>