diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index d035cb2e2..2425b7ec7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -62,6 +62,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper; +import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; @@ -145,6 +146,9 @@ public class GBApplication extends Application { private BluetoothStateChangeReceiver bluetoothStateChangeReceiver; private OpenTracksContentObserver openTracksObserver; + + private long lastAutoExportTimestamp = 0; + private long autoExportScheduledTimestamp = 0; public static void quit() { GB.log("Quitting Gadgetbridge...", GB.INFO, null); @@ -213,6 +217,8 @@ public class GBApplication extends Application { loadAppsPebbleBlackList(); loadCalendarsBlackList(); + PeriodicExporter.enablePeriodicExport(context); + if (isRunningMarshmallowOrLater()) { notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (isRunningOreoOrLater()) { @@ -1101,4 +1107,20 @@ public class GBApplication extends Application { public OpenTracksContentObserver getOpenTracksObserver() { return openTracksObserver; } + + public long getLastAutoExportTimestamp() { + return lastAutoExportTimestamp; + } + + public void setLastAutoExportTimestamp(long lastAutoExportTimestamp) { + this.lastAutoExportTimestamp = lastAutoExportTimestamp; + } + + public long getAutoExportScheduledTimestamp() { + return autoExportScheduledTimestamp; + } + + public void setAutoExportScheduledTimestamp(long autoExportScheduledTimestamp) { + this.autoExportScheduledTimestamp = autoExportScheduledTimestamp; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java index 145a9f7c4..2b526cce8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java @@ -42,6 +42,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; +import java.util.Date; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -51,6 +52,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -145,7 +147,7 @@ public class DataManagementActivity extends AbstractGBActivity { cleanExportDirectory(); } }); - + GBApplication gbApp = GBApplication.app(); Prefs prefs = GBApplication.getPrefs(); boolean autoExportEnabled = prefs.getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false); int autoExportInterval = prefs.getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0); @@ -153,16 +155,42 @@ public class DataManagementActivity extends AbstractGBActivity { //String autoExportLocation = prefs.getString(GBPrefs.AUTO_EXPORT_LOCATION, ""); int testExportVisibility = (autoExportInterval > 0 && autoExportEnabled) ? View.VISIBLE : View.GONE; - + boolean isExportEnabled = autoExportInterval > 0 && autoExportEnabled; TextView autoExportLocation_label = findViewById(R.id.autoExportLocation_label); autoExportLocation_label.setVisibility(testExportVisibility); - TextView autoExportLocation_intro = findViewById(R.id.autoExportLocation_intro); - autoExportLocation_intro.setVisibility(testExportVisibility); - TextView autoExportLocation_path = findViewById(R.id.autoExportLocation_path); autoExportLocation_path.setVisibility(testExportVisibility); - autoExportLocation_path.setText(getAutoExportLocationSummary()); + autoExportLocation_path.setText(getAutoExportLocationUserString() + " (" + getAutoExportLocationPreferenceString() + ")" ); + + TextView autoExportEnabled_label = findViewById(R.id.autoExportEnabled); + if (isExportEnabled) { + autoExportEnabled_label.setText(getString(R.string.activity_db_management_autoexport_enabled_yes)); + } else { + autoExportEnabled_label.setText(getString(R.string.activity_db_management_autoexport_enabled_no)); + } + + TextView autoExportScheduled = findViewById(R.id.autoExportScheduled); + autoExportScheduled.setVisibility(testExportVisibility); + long setAutoExportScheduledTimestamp = gbApp.getAutoExportScheduledTimestamp(); + if (setAutoExportScheduledTimestamp > 0) { + autoExportScheduled.setText(getString(R.string.activity_db_management_autoexport_scheduled_yes, + DateTimeUtils.formatDateTime(new Date(setAutoExportScheduledTimestamp)))); + } else { + autoExportScheduled.setText(getResources().getString(R.string.activity_db_management_autoexport_scheduled_no)); + } + + TextView autoExport_lastTime_label = findViewById(R.id.autoExport_lastTime_label); + long lastAutoExportTimestamp = gbApp.getLastAutoExportTimestamp(); + + autoExport_lastTime_label.setVisibility(View.GONE); + autoExport_lastTime_label.setText(getString(R.string.autoExport_lastTime_label, + DateTimeUtils.formatDateTime(new Date(lastAutoExportTimestamp)))); + + if (lastAutoExportTimestamp > 0) { + autoExport_lastTime_label.setVisibility(testExportVisibility); + autoExport_lastTime_label.setVisibility(testExportVisibility); + } final Context context = getApplicationContext(); Button testExportDBButton = findViewById(R.id.testExportDBButton); @@ -180,18 +208,25 @@ public class DataManagementActivity extends AbstractGBActivity { sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); } - - - //would rather re-use method of SettingsActivity... but lifecycle... - private String getAutoExportLocationSummary() { + private String getAutoExportLocationPreferenceString() { String autoExportLocation = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null); if (autoExportLocation == null) { return ""; } + return autoExportLocation; + } + + private String getAutoExportLocationUri() { + String autoExportLocation = getAutoExportLocationPreferenceString(); + if (autoExportLocation == null) { + return ""; + } Uri uri = Uri.parse(autoExportLocation); try { + return AndroidUtils.getFilePath(getApplicationContext(), uri); } catch (IllegalArgumentException e) { + LOG.error("getFilePath did not work, trying to resolve content provider path"); try { Cursor cursor = getContentResolver().query( uri, @@ -208,6 +243,13 @@ public class DataManagementActivity extends AbstractGBActivity { return ""; } + private String getAutoExportLocationUserString() { + String location = getAutoExportLocationUri(); + if (location == "") { + return getString(R.string.activity_db_management_autoexport_location); + } + return location; + } private boolean hasOldActivityDatabase() { return new DBHelper(this).existsDB("ActivityDatabase"); @@ -403,7 +445,7 @@ public class DataManagementActivity extends AbstractGBActivity { public void onClick(DialogInterface dialog, int which) { try { File externalFilesDir = FileUtils.getExternalFilesDir(); - String autoexportFile = getAutoExportLocationSummary(); + String autoexportFile = getAutoExportLocationUri(); for (File file : externalFilesDir.listFiles()) { if (file.isFile() && (!FileUtils.getExtension(file.toString()).toLowerCase().equals("gpx")) && //keep GPX files 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 695b06746..5970cc691 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -313,7 +313,7 @@ public class SettingsActivity extends AbstractSettingsActivity { Integer.valueOf((String) autoExportInterval)); preference.setSummary(summary); boolean auto_export_enabled = GBApplication.getPrefs().getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false); - PeriodicExporter.sheduleAlarm(getApplicationContext(), Integer.valueOf((String) autoExportInterval), auto_export_enabled); + PeriodicExporter.scheduleAlarm(getApplicationContext(), Integer.valueOf((String) autoExportInterval), auto_export_enabled); return true; } }); @@ -327,7 +327,7 @@ public class SettingsActivity extends AbstractSettingsActivity { @Override public boolean onPreferenceChange(Preference preference, Object autoExportEnabled) { int autoExportInterval = GBApplication.getPrefs().getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0); - PeriodicExporter.sheduleAlarm(getApplicationContext(), autoExportInterval, (boolean) autoExportEnabled); + PeriodicExporter.scheduleAlarm(getApplicationContext(), autoExportInterval, (boolean) autoExportEnabled); return true; } }); @@ -492,7 +492,7 @@ public class SettingsActivity extends AbstractSettingsActivity { .getPrefs().getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false); int autoExportPeriod = GBApplication .getPrefs().getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0); - PeriodicExporter.sheduleAlarm(getApplicationContext(), autoExportPeriod, autoExportEnabled); + PeriodicExporter.scheduleAlarm(getApplicationContext(), autoExportPeriod, autoExportEnabled); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java index 2fe98665e..e3d5e3568 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java @@ -44,24 +44,30 @@ public class PeriodicExporter extends BroadcastReceiver { public static void enablePeriodicExport(Context context) { Prefs prefs = GBApplication.getPrefs(); + GBApplication gbApp = GBApplication.app(); + long autoExportScheduled = gbApp.getAutoExportScheduledTimestamp(); boolean autoExportEnabled = prefs.getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false); Integer autoExportInterval = prefs.getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0); - sheduleAlarm(context, autoExportInterval, autoExportEnabled); + scheduleAlarm(context, autoExportInterval, autoExportEnabled && autoExportScheduled == 0); } - public static void sheduleAlarm(Context context, Integer autoExportInterval, boolean autoExportEnabled) { + public static void scheduleAlarm(Context context, Integer autoExportInterval, boolean autoExportEnabled) { Intent i = new Intent(context, PeriodicExporter.class); - PendingIntent pi = PendingIntent.getBroadcast(context, 0 , i, 0); + PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); am.cancel(pi); if (!autoExportEnabled) { + LOG.info("Not scheduling periodic export, either already scheduled or not enabled"); return; } int exportPeriod = autoExportInterval * 60 * 60 * 1000; if (exportPeriod == 0) { + LOG.info("Not scheduling periodic export, interval set to 0"); return; } - LOG.info("Enabling periodic export"); + LOG.info("Scheduling periodic export"); + GBApplication gbApp = GBApplication.app(); + gbApp.setAutoExportScheduledTimestamp(System.currentTimeMillis() + exportPeriod); am.setInexactRepeating( AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + exportPeriod, @@ -72,21 +78,46 @@ public class PeriodicExporter extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - LOG.info("Exporting DB"); - try (DBHandler dbHandler = GBApplication.acquireDB()) { - DBHelper helper = new DBHelper(context); - String dst = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null); - if (dst == null) { - LOG.info("Unable to export DB, export location not set"); - return; + LOG.info("Received command to export DB"); + createRefreshTask("Export database", context).execute(); + } + + protected RefreshTask createRefreshTask(String task, Context context) { + return new RefreshTask(task, context); + } + + public class RefreshTask extends DBAccess { + Context localContext; + + public RefreshTask(String task, Context context) { + super(task, context); + localContext = context; + } + + @Override + protected void doInBackground(DBHandler handler) { + LOG.info("Exporting DB in a background thread"); + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DBHelper helper = new DBHelper(localContext); + String dst = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null); + if (dst == null) { + LOG.info("Unable to export DB, export location not set"); + return; + } + Uri dstUri = Uri.parse(dst); + try (OutputStream out = localContext.getContentResolver().openOutputStream(dstUri)) { + helper.exportDB(dbHandler, out); + GBApplication gbApp = GBApplication.app(); + gbApp.setLastAutoExportTimestamp(System.currentTimeMillis()); + } + } catch (Exception ex) { + GB.updateExportFailedNotification(localContext.getString(R.string.notif_export_failed_title), localContext); + LOG.info("Exception while exporting DB: ", ex); } - Uri dstUri = Uri.parse(dst); - try (OutputStream out = context.getContentResolver().openOutputStream(dstUri)) { - helper.exportDB(dbHandler, out); - } - } catch (Exception ex) { - GB.updateExportFailedNotification(context.getString(R.string.notif_export_failed_title), context); - LOG.info("Exception while exporting DB: ", ex); + } + + @Override + protected void onPostExecute(Object o) { } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java index 54dabe4ae..3464ca3a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java @@ -579,39 +579,46 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem if (widget != null) { posY.setText(Integer.toString(widget.getPosY())); } - // Configure position preset buttons - Button btnTop = layout.findViewById(R.id.watchface_widget_preset_top); - btnTop.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - posX.setText("120"); - posY.setText("58"); + + class WidgetPosition{ + final int posX, posY, buttonResource, hintStringResource; + + public WidgetPosition(int posX, int posY, int buttonResource, int hintStringResource) { + this.posX = posX; + this.posY = posY; + this.buttonResource = buttonResource; + this.hintStringResource = hintStringResource; } - }); - Button btnBottom = layout.findViewById(R.id.watchface_widget_preset_bottom); - btnBottom.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - posX.setText("120"); - posY.setText("182"); + } + + WidgetPosition[] positions = new WidgetPosition[]{ + new WidgetPosition(120, 58, R.id.watchface_widget_preset_top, R.string.watchface_dialog_widget_preset_top), + new WidgetPosition(182, 120, R.id.watchface_widget_preset_right, R.string.watchface_dialog_widget_preset_right), + new WidgetPosition(120, 182, R.id.watchface_widget_preset_bottom, R.string.watchface_dialog_widget_preset_bottom), + new WidgetPosition(58, 120, R.id.watchface_widget_preset_left, R.string.watchface_dialog_widget_preset_left), + }; + + for(final WidgetPosition position : positions){ + Button btn = layout.findViewById(position.buttonResource); + btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + posX.setText(String.valueOf(position.posX)); + posY.setText(String.valueOf(position.posY)); + } + }); + } + + if(widget == null){ + int currentIndex = widgets.size(); + if(currentIndex < 4){ + WidgetPosition newPosition = positions[currentIndex]; + posX.setText(String.valueOf(newPosition.posX)); + posY.setText(String.valueOf(newPosition.posY)); + GB.toast(getString(R.string.watchface_dialog_pre_setting_position, getString(newPosition.hintStringResource)), Toast.LENGTH_SHORT, GB.INFO); } - }); - Button btnLeft = layout.findViewById(R.id.watchface_widget_preset_left); - btnLeft.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - posX.setText("58"); - posY.setText("120"); - } - }); - Button btnRight = layout.findViewById(R.id.watchface_widget_preset_right); - btnRight.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - posX.setText("182"); - posY.setText("120"); - } - }); + } + // Set widget size final LinearLayout sizeLayout = layout.findViewById(R.id.watchface_widget_size_layout); sizeLayout.setVisibility(View.GONE); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java index 208b90b0f..f5e916486 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java @@ -38,7 +38,7 @@ public class AutoStartReceiver extends BroadcastReceiver { } else { GBApplication.deviceService().start(); } - + Log.i(TAG, "Going to enable periodic exporter"); PeriodicExporter.enablePeriodicExport(context); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java index 51357d254..73fb64da3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java @@ -194,6 +194,9 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { batteryInfo.level = b; batteryInfo.state = BatteryState.BATTERY_NORMAL; } + if (json.has("chg") && json.getInt("chg") == 1) { + batteryInfo.state = BatteryState.BATTERY_CHARGING; + } if (json.has("volt")) batteryInfo.voltage = (float) json.getDouble("volt"); handleGBDeviceEvent(batteryInfo); @@ -367,9 +370,10 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { jsonalarms.put(jsonalarm); Calendar calendar = AlarmUtils.toCalendar(alarm); - // TODO: getRepetition to ensure it only happens on correct day? + jsonalarm.put("h", alarm.getHour()); jsonalarm.put("m", alarm.getMinute()); + jsonalarm.put("rep", alarm.getRepetition()); } uartTxJSON("onSetAlarms", o); } catch (JSONException e) { diff --git a/app/src/main/res/layout/activity_data_management.xml b/app/src/main/res/layout/activity_data_management.xml index d72cb5216..0c2489d00 100644 --- a/app/src/main/res/layout/activity_data_management.xml +++ b/app/src/main/res/layout/activity_data_management.xml @@ -100,7 +100,7 @@ android:text="@string/activity_db_management_clean_export_directory_label" /> + + + + + + + Verwende den Kopfhörer wie gewohnt. Wenn sich die Tragebedingungen oder der Luftdruck ändern, führe den Optimierer erneut aus. Amazfit Pop Amazfit Pop Pro + Setze position auf %s Bei neuen Benachrichtigungen aufleuchten %d Geräte verbunden diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 896cd3479..49a5a2316 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -760,7 +760,13 @@ Old Activity database deletion failed. Overwrite Database autoexport location has been set to: + Last AutoExport: %1$s + AutoExport is enabled. + AutoExport is not enabled. + AutoExport has (originally) been scheduled for %1$s + AutoExport has not been not scheduled. AutoExport + Location could not be understood. Likely an issue of newer Android permission system. Most likely, autoexport is not working now. Export Data Import Data Run AutoExport Now @@ -1521,6 +1527,7 @@ Width: OpenTracks package name Used for starting/stopping GPS track recording in external fitness app. + pre-setting position to %s Light up on new notification no devices connected diff --git a/app/src/nightly/res/values/ic_launcher_background.xml b/app/src/nightly/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..cdde88fb8 --- /dev/null +++ b/app/src/nightly/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #000000 + diff --git a/app/src/nopebble/res/values/ic_launcher_background.xml b/app/src/nopebble/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..ed621a247 --- /dev/null +++ b/app/src/nopebble/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #ff3d00 +