mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 02:46:50 +01:00
parent
b40c3ade85
commit
957d441859
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
BIN
app/src/main/res/drawable/widget_preview.png
Normal file
BIN
app/src/main/res/drawable/widget_preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
121
app/src/main/res/layout/widget.xml
Normal file
121
app/src/main/res/layout/widget.xml
Normal 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>
|
103
app/src/main/res/layout/widget_alarms_activity_list.xml
Normal file
103
app/src/main/res/layout/widget_alarms_activity_list.xml
Normal 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>
|
||||
|
||||
|
@ -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>
|
||||
|
14
app/src/main/res/xml/widget_info.xml
Normal file
14
app/src/main/res/xml/widget_info.xml
Normal 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>
|
||||
|
Loading…
Reference in New Issue
Block a user