diff --git a/CHANGELOG.md b/CHANGELOG.md index c34cdbdc9..19ab2fbbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ ###Changelog +####Version 0.10.1 +* Pebble: set extended music info by dissecting notifications on Android 5.0+ +* Pebble: various other improvemnts to music playback +* Pebble: allow ignoring activity trackers indiviually (to keep the data on the pebble) +* Mi Band: support for shifting the device time by N hours (for people who sleep at daytime) +* Mi Band: initial and untested support for Mi Band 2 +* Allow setting the application language + ####Version 0.10.0 * Pebble: option to send sunrise and sunset events to timeline * Pebble: fix problems with unknown app keys while configuring watchfaces diff --git a/app/build.gradle b/app/build.gradle index 82f0d9a12..607ee74b9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.10.0" - versionCode 53 + versionName "0.10.1" + versionCode 54 } buildTypes { release { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index f66bd7310..db758d3b0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -193,6 +193,9 @@ public class AppManagerActivity extends GBActivity { if (!selectedApp.isConfigurable()) { menu.removeItem(R.id.appmanager_app_configure); } + if (mGBDevice != null && !mGBDevice.getFirmwareVersion().startsWith("v3")) { + menu.removeItem(R.id.appmanager_app_move_to_top); + } menu.setHeaderTitle(selectedApp.getName()); } @@ -256,6 +259,9 @@ public class AppManagerActivity extends GBActivity { startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); startActivity(startIntent); return true; + case R.id.appmanager_app_move_to_top: + GBApplication.deviceService().onAppReorder(new UUID[]{selectedApp.getUUID()}); + return true; default: return super.onContextItemSelected(item); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index f53cf085e..c3526d7fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -222,6 +223,15 @@ public class DebugActivity extends GBActivity { musicSpec.trackNr = 2; GBApplication.deviceService().onSetMusicInfo(musicSpec); + + MusicStateSpec stateSpec = new MusicStateSpec(); + stateSpec.position = 0; + stateSpec.state = 0x01; // playing + stateSpec.playRate = 100; + stateSpec.repeat = 1; + stateSpec.shuffle = 1; + + GBApplication.deviceService().onSetMusicState(stateSpec); } }); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 602ca5c85..97e0f8f6a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -9,6 +9,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; /** @@ -25,6 +26,8 @@ public interface EventHandler { void onSetCallState(CallSpec callSpec); + void onSetMusicState(MusicStateSpec stateSpec); + void onSetMusicInfo(MusicSpec musicSpec); void onEnableRealtimeSteps(boolean enable); @@ -39,6 +42,8 @@ public interface EventHandler { void onAppConfiguration(UUID appUuid, String config); + void onAppReorder(UUID uuids[]); + void onFetchActivityData(); void onReboot(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 67c7e8e70..3edfb8815 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -3,30 +3,54 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.Bundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; public class MusicPlaybackReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(MusicPlaybackReceiver.class); + private static MusicSpec lastMusicSpec = new MusicSpec(); + private static MusicStateSpec lastStatecSpec = new MusicStateSpec(); @Override public void onReceive(Context context, Intent intent) { - String artist = intent.getStringExtra("artist"); - String album = intent.getStringExtra("album"); - String track = intent.getStringExtra("track"); - - LOG.info("Current track: " + artist + ", " + album + ", " + track); - + /* + Bundle bundle = intent.getExtras(); + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + LOG.info(String.format("%s %s (%s)", key, + value != null ? value.toString() : "null", value != null ? value.getClass().getName() : "no class")); + } + */ MusicSpec musicSpec = new MusicSpec(); - musicSpec.artist = artist; - musicSpec.album = album; - musicSpec.track = track; + musicSpec.artist = intent.getStringExtra("artist"); + musicSpec.album = intent.getStringExtra("album"); + musicSpec.track = intent.getStringExtra("track"); + musicSpec.duration = intent.getIntExtra("duration", 0) / 1000; - GBApplication.deviceService().onSetMusicInfo(musicSpec); + if (!lastMusicSpec.equals(musicSpec)) { + lastMusicSpec = musicSpec; + LOG.info("Update Music Info: " + musicSpec.artist + " / " + musicSpec.album + " / " + musicSpec.track); + GBApplication.deviceService().onSetMusicInfo(musicSpec); + } else { + LOG.info("got metadata changed intent, but nothing changed, ignoring."); + } + + if (intent.hasExtra("position") && intent.hasExtra("playing")) { + MusicStateSpec stateSpec = new MusicStateSpec(); + stateSpec.position = intent.getIntExtra("position", 0) / 1000; + stateSpec.state = (byte) (intent.getBooleanExtra("playing", true) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED); + if (!lastStatecSpec.equals(stateSpec)) { + LOG.info("Update Music State: state=" + stateSpec.state + ", position= " + stateSpec.position); + GBApplication.deviceService().onSetMusicState(stateSpec); + } else { + LOG.info("got state changed intent, but not enough has changed, ignoring."); + } + lastStatecSpec = stateSpec; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index a3c3d37c4..cf74be6a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -11,6 +11,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.os.Build; import android.os.Bundle; import android.os.PowerManager; import android.service.notification.NotificationListenerService; @@ -25,6 +30,8 @@ import org.slf4j.LoggerFactory; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; @@ -161,13 +168,6 @@ public class NotificationListener extends NotificationListenerService { return; } - Prefs prefs = GBApplication.getPrefs(); - if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { - PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); - if (powermanager.isScreenOn()) { - return; - } - } switch (GBApplication.getGrantedInterruptionFilter()) { case NotificationManager.INTERRUPTION_FILTER_ALL: break; @@ -182,6 +182,17 @@ public class NotificationListener extends NotificationListenerService { String source = sbn.getPackageName(); Notification notification = sbn.getNotification(); + if (handleMediaSessionNotification(notification)) + return; + + Prefs prefs = GBApplication.getPrefs(); + if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { + PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); + if (powermanager.isScreenOn()) { + return; + } + } + if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) { return; } @@ -311,6 +322,79 @@ public class NotificationListener extends NotificationListenerService { return false; } + /** + * Try to handle media session notifications that tell info about the current play state. + * + * @param notification The notification to handle. + * @return true if notification was handled, false otherwise + */ + public boolean handleMediaSessionNotification(Notification notification) { + + // this code requires Android 5.0 or newer + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return false; + } + + MusicSpec musicSpec = new MusicSpec(); + MusicStateSpec stateSpec = new MusicStateSpec(); + + Bundle extras = notification.extras; + if (extras == null) + return false; + + if (extras.get(Notification.EXTRA_MEDIA_SESSION) == null) + return false; + + MediaController c; + try { + c = new MediaController(getApplicationContext(), (MediaSession.Token) extras.get(Notification.EXTRA_MEDIA_SESSION)); + } catch (NullPointerException e) { + return false; + } + + PlaybackState s = c.getPlaybackState(); + stateSpec.position = (int) (s.getPosition() / 1000); + stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed()); + stateSpec.repeat = 1; + stateSpec.shuffle = 1; + switch (s.getState()) { + case PlaybackState.STATE_PLAYING: + stateSpec.state = MusicStateSpec.STATE_PLAYING; + break; + case PlaybackState.STATE_STOPPED: + stateSpec.state = MusicStateSpec.STATE_STOPPED; + break; + case PlaybackState.STATE_PAUSED: + stateSpec.state = MusicStateSpec.STATE_PAUSED; + break; + default: + stateSpec.state = MusicStateSpec.STATE_UNKNOWN; + break; + } + + MediaMetadata d = c.getMetadata(); + if (d == null) + return false; + if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) + musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST); + if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) + musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM); + if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE)) + musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE); + if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION)) + musicSpec.duration = (int)d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000; + if (d.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) + musicSpec.trackCount = (int)d.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS); + if (d.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) + musicSpec.trackNr = (int)d.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); + + // finally, tell the device about it + GBApplication.deviceService().onSetMusicInfo(musicSpec); + GBApplication.deviceService().onSetMusicState(stateSpec); + + return true; + } + @Override public void onNotificationRemoved(StatusBarNotification sbn) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 2e598eacf..1aa83c9c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -14,9 +14,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; +//import java.util.UUID; + public class GBDeviceService implements DeviceService { protected final Context mContext; protected final Class mServiceClass; @@ -125,6 +128,17 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + Intent intent = createIntent().setAction(ACTION_SETMUSICSTATE) + .putExtra(EXTRA_MUSIC_REPEAT, stateSpec.repeat) + .putExtra(EXTRA_MUSIC_RATE, stateSpec.playRate) + .putExtra(EXTRA_MUSIC_STATE, stateSpec.state) + .putExtra(EXTRA_MUSIC_SHUFFLE, stateSpec.shuffle) + .putExtra(EXTRA_MUSIC_POSITION, stateSpec.position); + invokeService(intent); + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { Intent intent = createIntent().setAction(ACTION_SETMUSICINFO) @@ -173,6 +187,13 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onAppReorder(UUID[] uuids) { + Intent intent = createIntent().setAction(ACTION_APP_REORDER) + .putExtra(EXTRA_APP_UUID, uuids); + invokeService(intent); + } + @Override public void onFetchActivityData() { Intent intent = createIntent().setAction(ACTION_FETCH_ACTIVITY_DATA); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index aaf02798b..59518f7ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -18,12 +18,14 @@ public interface DeviceService extends EventHandler { String ACTION_CALLSTATE = PREFIX + ".action.callstate"; String ACTION_SETTIME = PREFIX + ".action.settime"; String ACTION_SETMUSICINFO = PREFIX + ".action.setmusicinfo"; + String ACTION_SETMUSICSTATE = PREFIX + ".action.setmusicstate"; String ACTION_REQUEST_DEVICEINFO = PREFIX + ".action.request_deviceinfo"; String ACTION_REQUEST_APPINFO = PREFIX + ".action.request_appinfo"; String ACTION_REQUEST_SCREENSHOT = PREFIX + ".action.request_screenshot"; String ACTION_STARTAPP = PREFIX + ".action.startapp"; String ACTION_DELETEAPP = PREFIX + ".action.deleteapp"; String ACTION_APP_CONFIGURE = PREFIX + ".action.app_configure"; + String ACTION_APP_REORDER = PREFIX + ".action.app_reorder"; String ACTION_INSTALL = PREFIX + ".action.install"; String ACTION_REBOOT = PREFIX + ".action.reboot"; String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test"; @@ -57,6 +59,11 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_DURATION = "music_duration"; String EXTRA_MUSIC_TRACKNR = "music_tracknr"; String EXTRA_MUSIC_TRACKCOUNT = "music_trackcount"; + String EXTRA_MUSIC_STATE = "music_state"; + String EXTRA_MUSIC_SHUFFLE = "music_shuffle"; + String EXTRA_MUSIC_REPEAT = "music_repeat"; + String EXTRA_MUSIC_POSITION = "music_position"; + String EXTRA_MUSIC_RATE = "music_rate"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; String EXTRA_APP_CONFIG = "app_config"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java index af52f02f1..aa9f67ca9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.model; +import java.util.Objects; + public class MusicSpec { public static final int MUSIC_UNDEFINED = 0; public static final int MUSIC_PLAY = 1; @@ -14,4 +16,23 @@ public class MusicSpec { public int duration; public int trackCount; public int trackNr; + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof MusicSpec)) { + return false; + } + MusicSpec musicSpec = (MusicSpec) obj; + + return Objects.equals(this.artist, musicSpec.artist) && + Objects.equals(this.album, musicSpec.album) && + Objects.equals(this.track, musicSpec.track) && + this.duration == musicSpec.duration && + this.trackCount == musicSpec.trackCount && + this.trackNr == musicSpec.trackNr; + + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java new file mode 100644 index 000000000..fb15948d7 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java @@ -0,0 +1,34 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +/** + * Created by steffen on 07.06.16. + */ +public class MusicStateSpec { + public static final int STATE_PLAYING = 0; + public static final int STATE_PAUSED = 1; + public static final int STATE_STOPPED = 2; + public static final int STATE_UNKNOWN = 3; + + public byte state; + public int position; + public int playRate; + public byte shuffle; + public byte repeat; + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof MusicStateSpec)) { + return false; + } + MusicStateSpec stateSpec = (MusicStateSpec) obj; + + return this.state == stateSpec.state && + Math.abs(this.position - stateSpec.position)<=2 && + this.playRate == stateSpec.playRate && + this.shuffle == stateSpec.shuffle && + this.repeat == stateSpec.repeat; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index fdafa5265..f66ed5041 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; @@ -47,6 +48,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_REORDER; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; @@ -65,6 +67,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_RE import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_DEVICEINFO; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_SCREENSHOT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETMUSICINFO; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETMUSICSTATE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETTIME; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_ALARMS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_START; @@ -87,6 +90,11 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIN import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_DURATION; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_POSITION; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_RATE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_REPEAT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_SHUFFLE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_STATE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACK; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKCOUNT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKNR; @@ -351,6 +359,15 @@ public class DeviceCommunicationService extends Service implements SharedPrefere musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); mDeviceSupport.onSetMusicInfo(musicSpec); break; + case ACTION_SETMUSICSTATE: + MusicStateSpec stateSpec = new MusicStateSpec(); + stateSpec.shuffle = intent.getByteExtra(EXTRA_MUSIC_SHUFFLE, (byte)0); + stateSpec.repeat = intent.getByteExtra(EXTRA_MUSIC_REPEAT, (byte)0); + stateSpec.position = intent.getIntExtra(EXTRA_MUSIC_POSITION, 0); + stateSpec.playRate = intent.getIntExtra(EXTRA_MUSIC_RATE, 0); + stateSpec.state = intent.getByteExtra(EXTRA_MUSIC_STATE, (byte)0); + mDeviceSupport.onSetMusicState(stateSpec); + break; case ACTION_REQUEST_APPINFO: mDeviceSupport.onAppInfoReq(); break; @@ -372,6 +389,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere UUID uuid = (UUID) intent.getSerializableExtra(EXTRA_APP_UUID); String config = intent.getStringExtra(EXTRA_APP_CONFIG); mDeviceSupport.onAppConfiguration(uuid, config); + break; + } + case ACTION_APP_REORDER: { + UUID[] uuids = (UUID[]) intent.getSerializableExtra(EXTRA_APP_UUID); + mDeviceSupport.onAppReorder(uuids); + break; } case ACTION_INSTALL: Uri uri = intent.getParcelableExtra(EXTRA_URI); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 019722b54..bf43077be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -16,6 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; /** @@ -150,6 +151,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onSetCallState(callSpec); } + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + if (checkBusy("set music state")) { + return; + } + delegate.onSetMusicState(stateSpec); + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { if (checkBusy("set music info")) { @@ -198,6 +207,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onAppConfiguration(uuid, config); } + @Override + public void onAppReorder(UUID[] uuids) { + if (checkBusy("app reorder")) { + return; + } + delegate.onAppReorder(uuids); + } + @Override public void onFetchActivityData() { if (checkBusy("fetch activity data")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index fd00d02ac..fbb73cc5d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; 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.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; @@ -599,6 +600,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return telephoneRinging; } + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + // not supported + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { // not supported @@ -761,6 +767,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { // not supported } + @Override + public void onAppReorder(UUID[] uuids) { + // not supported + } + @Override public void onScreenshotReq() { // not supported diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 65f28a5e4..1e4953e1a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -18,7 +18,6 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.PebbleActivitySample; @@ -47,8 +46,7 @@ public class AppMessageHandlerMisfit extends AppMessageHandler { @Override public boolean isEnabled() { Prefs prefs = GBApplication.getPrefs(); - int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); - return (activityTracker == SampleProvider.PROVIDER_PEBBLE_MISFIT); + return prefs.getBoolean("pebble_sync_misfit", true); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 7bdc6a901..36e751d8b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -62,8 +62,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { @Override public boolean isEnabled() { Prefs prefs = GBApplication.getPrefs(); - int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); - return (activityTracker == SampleProvider.PROVIDER_PEBBLE_MORPHEUZ); + return prefs.getBoolean("pebble_sync_morpheuz", true); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java index acbbe9f7d..6df7a7514 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java @@ -3,7 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; abstract class DatalogSessionPebbleHealth extends DatalogSession { @@ -14,7 +13,6 @@ abstract class DatalogSessionPebbleHealth extends DatalogSession { protected boolean isPebbleHealthEnabled() { Prefs prefs = GBApplication.getPrefs(); - int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); - return (activityTracker == SampleProvider.PROVIDER_PEBBLE_HEALTH); + return prefs.getBoolean("pebble_sync_health", true); } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 2b0069a9d..1f36c0362 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -36,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; @@ -70,6 +71,7 @@ public class PebbleProtocol extends GBDeviceProtocol { static final short ENDPOINT_RUNKEEPER = 7000; static final short ENDPOINT_SCREENSHOT = 8000; static final short ENDPOINT_NOTIFICATIONACTION = 11440; // 3.x only, TODO: find a better name + static final short ENDPOINT_APPREORDER = (short) 0xabcd; // 3.x only static final short ENDPOINT_BLOBDB = (short) 45531; // 3.x only static final short ENDPOINT_PUTBYTES = (short) 48879; @@ -1103,6 +1105,20 @@ public class PebbleProtocol extends GBDeviceProtocol { } public byte[] encodeSetMusicState(byte state, int position, int playRate, byte shuffle, byte repeat) { + byte playState; + + switch (state) { + case MusicStateSpec.STATE_PLAYING: + playState = MUSICCONTROL_STATE_PLAYING; + break; + case MusicStateSpec.STATE_PAUSED: + playState = MUSICCONTROL_STATE_PAUSED; + break; + default: + playState = MUSICCONTROL_STATE_UNKNOWN; + break; + } + int length = LENGTH_PREFIX + 12; // Encode Prefix ByteBuffer buf = ByteBuffer.allocate(length); @@ -1112,8 +1128,8 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); buf.put(MUSICCONTROL_SETPLAYSTATE); - buf.put(state); - buf.putInt(position); + buf.put(playState); + buf.putInt(position * 1000); buf.putInt(playRate); buf.put(shuffle); buf.put(repeat); @@ -1127,7 +1143,6 @@ public class PebbleProtocol extends GBDeviceProtocol { if (duration == 0) { return encodeMessage(ENDPOINT_MUSICCONTROL, MUSICCONTROL_SETMUSICINFO, 0, parts); } else { - byte[] stateMessage = encodeSetMusicState(MUSICCONTROL_STATE_PLAYING, 0, 100, (byte) 1, (byte) 1); // Calculate length first int length = LENGTH_PREFIX + 9; if (parts != null) { @@ -1141,7 +1156,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } // Encode Prefix - ByteBuffer buf = ByteBuffer.allocate(length + stateMessage.length); + ByteBuffer buf = ByteBuffer.allocate(length); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort((short) (length - LENGTH_PREFIX)); buf.putShort(ENDPOINT_MUSICCONTROL); @@ -1165,8 +1180,6 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putShort((short) (trackCount & 0xffff)); buf.putShort((short) (trackNr & 0xffff)); - buf.put(stateMessage); - return buf.array(); } } @@ -1281,6 +1294,23 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeSimpleMessage(ENDPOINT_SCREENSHOT, SCREENSHOT_TAKE); } + @Override + public byte[] encodeAppReorder(UUID[] uuids) { + int length = 2 + uuids.length * LENGTH_UUID; + ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) length); + buf.putShort(ENDPOINT_APPREORDER); + buf.put((byte) 0x01); + buf.put((byte) uuids.length); + for (UUID uuid : uuids) { + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); + } + + return buf.array(); + } + /* pebble specific install methods */ public byte[] encodeUploadStart(byte type, int app_id, int size, String filename) { short length; @@ -1936,6 +1966,16 @@ public class PebbleProtocol extends GBDeviceProtocol { return sendBytes; } + private GBDeviceEvent decodeAppReorder(ByteBuffer buf) { + byte status = buf.get(); + if (status == 1) { + LOG.info("app reordering successful"); + } else { + LOG.info("app reordering returned status " + status); + } + return null; + } + @Override public GBDeviceEvent[] decodeResponse(byte[] responseData) { ByteBuffer buf = ByteBuffer.wrap(responseData); @@ -2186,6 +2226,8 @@ public class PebbleProtocol extends GBDeviceProtocol { case ENDPOINT_BLOBDB: devEvts = new GBDeviceEvent[]{decodeBlobDb(buf)}; break; + case ENDPOINT_APPREORDER: + devEvts = new GBDeviceEvent[]{decodeAppReorder(buf)}; default: break; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java index 042874d3a..9bf905b2c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; @@ -108,6 +109,13 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { } } + @Override + public void onSetMusicState(MusicStateSpec musicStateSpec) { + if (reconnect()) { + super.onSetMusicState(musicStateSpec); + } + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { if (reconnect()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 78e15667d..0e4f5bbff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -11,6 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; @@ -123,6 +124,12 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport sendToDevice(bytes); } + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + byte[] bytes = gbDeviceProtocol.encodeSetMusicState(stateSpec.state, stateSpec.position, stateSpec.playRate, stateSpec.shuffle, stateSpec.repeat); + sendToDevice(bytes); + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(musicSpec.artist, musicSpec.album, musicSpec.track, musicSpec.duration, musicSpec.trackCount, musicSpec.trackNr); @@ -147,6 +154,12 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport sendToDevice(bytes); } + @Override + public void onAppReorder(UUID[] uuids) { + byte[] bytes = gbDeviceProtocol.encodeAppReorder(uuids); + sendToDevice(bytes); + } + @Override public void onFetchActivityData() { byte[] bytes = gbDeviceProtocol.encodeSynchronizeActivityData(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index c12c65675..ec641e361 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -31,6 +31,10 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeSetMusicState(byte state, int position, int playRate, byte shuffle, byte repeat) { + return null; + } + public byte[] encodeFirmwareVersionReq() { return null; } @@ -51,6 +55,10 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeAppReorder(UUID[] uuids) { + return null; + } + public byte[] encodeSynchronizeActivityData() { return null; } diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index 873e0c246..72cb29300 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -18,4 +18,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6ca7ee3b6..3f0dc77a6 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -238,5 +238,4 @@ Firmware non inviato Battito cardiaco Battito cardiaco - Offset orologio del dispositivo in ore (per l\'identificazione del sonno dei lavoratori a turni) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ccdfc41a4..2ec72ca73 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -37,6 +37,7 @@ テーマ ライト ダーク + 言語 通知 繰り返し 電話通知 @@ -57,7 +58,11 @@ 開発者用設定 Mi Bandのアドレス Pebbleの設定 + アクティビティ トラッカー お好みのアクティビティ トラッカー + Pebble Health 同期 + Misfit 同期 + Morpheuz 同期 第三者のアンドロイドアップにアクセス権利を与える PebbleKitを使用してAndroidアプリ用の実験的なサポートを有効にします 日の出と日の入り @@ -214,6 +219,7 @@ このファームウェアは、デバイスと互換性がありません 今後のイベントのために予約するアラーム 睡眠の検出を改善するために心拍センサーを使用する + デバイスの時刻オフセット時間 (交代勤務の睡眠検知用) 再接続の待機中 再インストール あなたについて diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 7f79179f6..766b99bb8 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -34,6 +34,7 @@ Тема Світла Темна + Мова Сповіщення Повтори Виклики @@ -203,4 +204,6 @@ Вимкнути Конфігурація Додати віджет + Пристрій: %1$s + ПЗ: %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 988e59050..3bc390d94 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -74,7 +74,13 @@ Mi Band address Pebble Settings + + Activity Trackers Preferred Activitytracker + Sync Pebble Health + Sync Misfit + Sync Morpheuz + Allow 3rd Party Android App Access Enable experimental support for Android Apps using PebbleKit @@ -251,9 +257,11 @@ Weight in kg Activate Deactivate + Configure + Move to top + authenticating authentication required - Configure Zzz Add widget diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index a49185489..30d19573d 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,13 @@ + + Pebble: set extended music info by dissecting notifications on Android 5.0+ + Pebble: various other improvemnts to music playback + Pebble: allow ignoring activity trackers indiviually (to keep the data on the pebble) + Mi Band: support for shifting the device time by N hours (for people who sleep at daytime) + Mi Band: initial and untested support for Mi Band 2 + Allow setting the application language + Pebble: option to send sunrise and sunset events to timeline Pebble: fix problems with unknown app keys while configuring watchfaces diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index aa7843311..7c5723b6b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -215,17 +215,31 @@ android:key="pebble_reconnect_attempts" android:maxLength="4" android:title="@string/pref_title_pebble_reconnect_attempts" /> + + + + android:summary="%s" + android:title="@string/pref_title_pebble_activitytracker" /> + android:defaultValue="true" + android:key="pebble_sync_health" + android:title="@string/pref_title_pebble_sync_health" /> + > + diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 8070cb1eb..241193f7a 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -12,6 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; public class TestDeviceSupport extends AbstractDeviceSupport { @@ -66,6 +67,11 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + @Override public void onSetMusicInfo(MusicSpec musicSpec) { @@ -96,6 +102,11 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } + @Override + public void onAppReorder(UUID[] uuids) { + + } + @Override public void onFetchActivityData() { diff --git a/build.gradle b/build.gradle index 819874a7d..86fd2bad3 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.1.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files