1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-28 12:56:49 +01:00

Fossil Hybrid HR: Sync notification dismissals to watch (#2219)

Fossil Hybrid HR: Move check for autoremove_notifications pref to NotificationListener

Fossil Hybrid: Rename logger to LOG and replace printStackTrace calls

Keep and maintain list of notifications pushed to device

Fossil Hybrid HR: Make autoremove notifications toggleable in device settings

Fossil Hybrid HR: Sync notification dismissals to watch

Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2219
Co-Authored-By: Arjan Schrijver <arjan5@noreply.codeberg.org>
Co-Committed-By: Arjan Schrijver <arjan5@noreply.codeberg.org>
This commit is contained in:
Arjan Schrijver 2021-03-08 14:29:08 +01:00 committed by Andreas Shimokawa
parent b4a115d2c3
commit cff4b65fb8
9 changed files with 98 additions and 41 deletions

View File

@ -54,6 +54,7 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_FIND_PHONE_ENABLED = "prefs_find_phone";
public static final String PREF_AUTOLIGHT = "autolight";
public static final String PREF_AUTOREMOVE_MESSAGE = "autoremove_message";
public static final String PREF_AUTOREMOVE_NOTIFICATIONS = "autoremove_notifications";
public static final String PREF_OPERATING_SOUNDS = "operating_sounds";
public static final String PREF_KEY_VIBRATION = "key_vibration";
public static final String PREF_FAKE_RING_DURATION = "fake_ring_duration";

View File

@ -47,6 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALTITUDE_CALIBRATE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AMPM_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTILOST_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_NOTIFICATIONS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BT_CONNECTED_ADVERTISEMENT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOLIGHT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_MESSAGE;
@ -390,6 +391,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
addPreferenceHandlerFor(PREF_FIND_PHONE_ENABLED);
addPreferenceHandlerFor(PREF_AUTOLIGHT);
addPreferenceHandlerFor(PREF_AUTOREMOVE_MESSAGE);
addPreferenceHandlerFor(PREF_AUTOREMOVE_NOTIFICATIONS);
addPreferenceHandlerFor(PREF_KEY_VIBRATION);
addPreferenceHandlerFor(PREF_OPERATING_SOUNDS);
addPreferenceHandlerFor(PREF_FAKE_RING_DURATION);

View File

@ -54,6 +54,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -71,6 +72,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterDao;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntry;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntryDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
@ -115,6 +117,7 @@ public class NotificationListener extends NotificationListenerService {
);
public static ArrayList<String> notificationStack = new ArrayList<>();
private static ArrayList<Integer> notificationsActive = new ArrayList<Integer>();
private long activeCallPostTime;
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
@ -239,6 +242,7 @@ public class NotificationListener extends NotificationListenerService {
public void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
notificationStack.clear();
notificationsActive.clear();
super.onDestroy();
}
@ -423,6 +427,7 @@ public class NotificationListener extends NotificationListenerService {
}else {
LOG.info("This app might show old/duplicate notifications. notification.when is 0 for " + source);
}
notificationsActive.add(notificationSpec.getId());
GBApplication.deviceService().onNotification(notificationSpec);
}
@ -720,20 +725,41 @@ public class NotificationListener extends NotificationListenerService {
GBApplication.deviceService().onSetCallState(callSpec);
}
}
// FIXME: DISABLED for now
if (shouldIgnoreNotification(sbn, true)) return;
Prefs prefs = GBApplication.getPrefs();
if (prefs.getBoolean("autoremove_notifications", true)) {
LOG.info("notification removed, will ask device to delete it");
Object o = mNotificationHandleLookup.lookupByValue(sbn.getPostTime());
// Build list of all currently active notifications
ArrayList<Integer> activeNotificationsIds = new ArrayList<Integer>();
for (StatusBarNotification notification : getActiveNotifications()) {
Object o = mNotificationHandleLookup.lookupByValue(notification.getPostTime());
if(o != null) {
int id = (int) o;
GBApplication.deviceService().onDeleteNotification(id);
activeNotificationsIds.add(id);
}
}
// Build list of notifications that aren't active anymore
ArrayList<Integer> notificationsToRemove = new ArrayList<Integer>();
for (int notificationId : notificationsActive) {
if (!activeNotificationsIds.contains(notificationId)) {
notificationsToRemove.add(notificationId);
}
}
// Clean up removed notifications from internal list
notificationsActive.removeAll(notificationsToRemove);
// Send notification remove request to device
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if (connectedDevice != null) {
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(connectedDevice.getAddress()));
if (prefs.getBoolean("autoremove_notifications", true)) {
for (int id : notificationsToRemove) {
LOG.info("Notification " + id + " removed, will ask device to delete it");
GBApplication.deviceService().onDeleteNotification(id);
}
}
}
}
private void logNotification(StatusBarNotification sbn, boolean posted) {

View File

@ -105,7 +105,7 @@ public class FossilWatchAdapter extends WatchAdapter {
private int lastButtonIndex = -1;
protected Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
protected final Logger LOG = LoggerFactory.getLogger(getClass().getSimpleName());
SupportedFileVersionsInfo supportedFileVersions;
@ -740,7 +740,7 @@ public class FossilWatchAdapter extends WatchAdapter {
}
private void log(String message) {
logger.debug(message);
LOG.debug(message);
}
public void queueWrite(SetDeviceStateRequest request, boolean priorise) {

View File

@ -68,13 +68,11 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HybridHRActivitySample;
import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
@ -88,6 +86,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileLookupRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRawRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.DismissTextNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayCallNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayTextNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationInformation;
@ -284,7 +283,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
try {
this.backGroundImage = AssetImageFactory.createAssetImage(whiteBitmap, true, 0, 1, 0);
} catch (IOException e2) {
logger.error("Backgroundimage error", e2);
LOG.error("Backgroundimage error", e2);
}
}
} catch (IOException | RuntimeException e) {
@ -382,7 +381,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
widgets.add(widget);
}
} catch (JSONException e) {
e.printStackTrace();
LOG.error("Error while updating widgets", e);
}
for (Widget oldWidget : oldWidgets) {
@ -508,12 +507,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
File imageFile = new File(element.getValue());
if (!imageFile.exists() || !imageFile.isFile()) {
logger.debug("Image file " + element.getValue() + " not found");
LOG.debug("Image file " + element.getValue() + " not found");
continue;
}
Bitmap imageBitmap = BitmapFactory.decodeFile(element.getValue());
if (imageBitmap == null) {
logger.debug("image file " + element.getValue() + " could not be decoded");
LOG.debug("image file " + element.getValue() + " could not be decoded");
continue;
}
Bitmap scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 76, 76, false);
@ -578,7 +577,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
this
));
} catch (IOException e) {
e.printStackTrace();
LOG.error("Error while rendering widgets", e);
}
}
@ -592,7 +591,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
resultIntent.putExtra("EXTRA_SUCCESS", true);
resultIntent.putExtra("EXTRA_PATH", outputFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
LOG.error("Error while downloading file", e);
resultIntent.putExtra("EXTRA_SUCCESS", false);
}
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(resultIntent);
@ -609,7 +608,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
fis.read(fileData);
fis.close();
} catch (IOException e) {
e.printStackTrace();
LOG.error("Error while reading file", e);
resultIntent.putExtra("EXTRA_SUCCESS", false);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(resultIntent);
return;
@ -654,7 +653,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
listApplications();
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("Error while uploading file", e);
resultIntent.putExtra("EXTRA_SUCCESS", false);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(resultIntent);
}
@ -666,7 +665,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite((FileEncryptedInterface) new FileEncryptedGetRequest(handle, this) {
@Override
public void handleFileData(byte[] fileData) {
logger.debug("downloaded encrypted file");
LOG.debug("downloaded encrypted file");
handleFileDownload(handle, fileData);
}
});
@ -674,7 +673,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new FileGetRawRequest(handle, this) {
@Override
public void handleFileRawData(byte[] fileData) {
logger.debug("downloaded regular file");
LOG.debug("downloaded regular file");
handleFileDownload(handle, fileData);
}
});
@ -892,7 +891,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
queueWrite(new PlayTextNotificationRequest("generic", senderOrTitle, notificationSpec.body, notificationSpec.getId(), this));
} catch (Exception e) {
e.printStackTrace();
LOG.error("Error while forwarding notification", e);
}
if (isNotificationWidgetVisible() && sourceAppId != null) {
@ -908,7 +907,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
appIconCache.put(sourceAppId, iconBitmap);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
LOG.error("Error while updating notification widget", e);
}
}
this.lastPostedApp = sourceAppId;
@ -922,6 +921,13 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
public void onDeleteNotification(int id) {
super.onDeleteNotification(id);
// send notification dismissal message to watch
try {
queueWrite(new DismissTextNotificationRequest(id, this));
} catch (Exception e) {
LOG.error("Error while dismissing notification", e);
}
// only delete app icon when no notification of said app is present
for (String app : NotificationListener.notificationStack) {
if (app.equals(this.lastPostedApp)) return;
@ -1063,7 +1069,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new JsonPutRequest(forecastResponseObject, this));
} catch (JSONException e) {
logger.error("JSON exception: ", e);
LOG.error("JSON exception: ", e);
}
}
@ -1200,7 +1206,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
}
} catch (JSONException e) {
e.printStackTrace();
LOG.error("Error while configuring buttons", e);
}
}
@ -1243,7 +1249,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
int heartRate = value[1];
logger.debug("heart rate: " + heartRate);
LOG.debug("heart rate: " + heartRate);
}
@Override
@ -1264,7 +1270,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
handleMusicRequest(value);
} else if (requestType == (byte) 0x01) {
int eventId = value[2];
logger.info("got event id " + eventId);
LOG.info("got event id " + eventId);
try {
String jsonString = new String(value, 3, value.length - 3);
// logger.info(jsonString);
@ -1275,7 +1281,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
if (request.has("ringMyPhone")) {
String action = request.getJSONObject("ringMyPhone").getString("action");
logger.info("got ringMyPhone request; " + action);
LOG.info("got ringMyPhone request; " + action);
GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
JSONObject responseObject = new JSONObject()
@ -1307,12 +1313,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new JsonPutRequest(responseObject, this));
}
} else if (request.has("weatherInfo") || request.has("weatherApp._.config.locations")) {
logger.info("Got weatherInfo request");
LOG.info("Got weatherInfo request");
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
if (weatherSpec != null) {
onSendWeather(weatherSpec);
} else {
logger.info("no weather data available - ignoring request");
LOG.info("no weather data available - ignoring request");
}
} else if (request.has("commuteApp._.config.commute_info")) {
String action = request.getJSONObject("commuteApp._.config.commute_info")
@ -1334,10 +1340,10 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
} else if (request.has("master._.config.app_status")) {
queueWrite(new ConfirmAppStatusRequest(requestId, this));
} else {
logger.warn("Unhandled request from watch: " + requestJson.toString());
LOG.warn("Unhandled request from watch: " + requestJson.toString());
}
} catch (JSONException e) {
e.printStackTrace();
LOG.error("Error while handling received characteristic", e);
}
}
}
@ -1365,7 +1371,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
private void handleMusicRequest(byte[] value) {
byte command = value[3];
logger.info("got music command: " + command);
LOG.info("got music command: " + command);
MUSIC_WATCH_REQUEST request = MUSIC_WATCH_REQUEST.fromCommandByte(command);
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();

View File

@ -0,0 +1,25 @@
/* Copyright (C) 2021 Arjan Schrijver
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.service.devices.qhybrid.requests.fossil.notification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
public class DismissTextNotificationRequest extends PlayNotificationRequest {
public DismissTextNotificationRequest(int messageId, FossilWatchAdapter adapter) {
super(7, 2, "", "", "", messageId, adapter);
}
}

View File

@ -166,7 +166,7 @@
<string name="pref_title_custom_deviceicon">Show device specific notification icon</string>
<string name="pref_summary_custom_deviceicon">Show a device specific Android notification icon instead the Gadgetbridge icon when connected</string>
<string name="pref_title_autoremove_notifications">Autoremove dismissed notifications</string>
<string name="pref_summary_autoremove_notifications">Notifications are automatically removed from the Pebble when dismissed from the Android device</string>
<string name="pref_summary_autoremove_notifications">Notifications are automatically removed from the device when dismissed from the phone</string>
<string name="pref_title_pebble_privacy_mode">Privacy mode</string>
<string name="pref_pebble_privacy_mode_off">Normal notifications</string>
<string name="pref_pebble_privacy_mode_content">Shift the notification text off-screen</string>

View File

@ -80,6 +80,11 @@
android:defaultValue="false"
android:key="save_raw_activity_files"
android:title="@string/pref_qhybrid_save_raw_activity_files" />
<SwitchPreference
android:defaultValue="true"
android:key="autoremove_notifications"
android:summary="@string/pref_summary_autoremove_notifications"
android:title="@string/pref_title_autoremove_notifications" />
<SeekBarPreference
android:defaultValue="2"

View File

@ -235,14 +235,6 @@
android:key="pebble_reconnect_attempts"
android:maxLength="4"
android:title="@string/pref_title_pebble_reconnect_attempts" />
<!--
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="autoremove_notifications"
android:summary="@string/pref_summary_autoremove_notifications"
android:title="@string/pref_title_autoremove_notifications" />
-->
<ListPreference
android:key="pebble_pref_privacy_mode"
android:title="@string/pref_title_pebble_privacy_mode"