Merge branch 'master' into feature-weather

here
Andreas Shimokawa 2016-05-24 11:09:21 +02:00
commit ec154c9041
34 changed files with 465 additions and 180 deletions

View File

@ -1,4 +1,12 @@
###Changelog
####Version 0.9.8
* Pebble: fix more reconnnect issues
* Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health
* Pebble: option in AppManager to delete files from cache
* Pebble: enable pbw cache and watchface configuration for Firmware 2.x
* Pebble: allow enabling of Pebble Health without "untested features" being enabled
* Honour "Do Not Disturb" for phone calls and SMS
####Version 0.9.7
* Pebble: hopefully fix some reconnect issues
* Mi Band: fix live activity monitoring running forever if back button pressed

28
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,28 @@
Andreas Shimokawa <shimokawa@fsfe.org>
cpfeiffer <cpfeiffer@users.noreply.github.com>
Daniele Gobbetti <daniele+github@gobbetti.name>
Daniele Gobbetti <daniele@gobbetti.name>
danielegobbetti <daniele+github@gobbetti.name>
Carsten Pfeiffer <cpfeiffer@users.noreply.github.com>
Julien Pivotto <roidelapluie@inuits.eu>
Lem Dulfo <lemuel.dulfo@gmail.com>
Sergey Trofimov <sarg@sarg.org.ru>
Daniele Gobbetti <daniele.gobbetti@gmail.com>
cpfeiffer <cpfeiffer@users.github.com>
0nse <0nse@users.noreply.github.com>
Christian Fischer <sw-dev@computerlyrik.de>
Normano64 <per.bergqwist@gmail.com>
Ⲇⲁⲛⲓ Φi <daniphii@outlook.com>
xphnx <xphnx@users.noreply.github.com>
Tarik Sekmen <tarik@ilixi.org>
rober <rober@prtl.nodomain.net>
Nicolò Balzarotti <anothersms@gmail.com>
Marc Schlaich <marc.schlaich@googlemail.com>
kevlarcade <kevlarcade@gmail.com>
Kasha <kasha_malaga@hotmail.com>
Chris Perelstein <chris.perelstein@gmail.com>
Alexey Afanasev <avafanasiev@gmail.com>
And all the Transifex translators, which I cannot automatically list, at the moment.
git log --raw | grep "^Author: " | sort | uniq -c | sort -k 1 -n -r | cut -f 2- -d: > CONTRIBUTORS.md

View File

@ -16,8 +16,8 @@ android {
targetSdkVersion 23
// note: always bump BOTH versionCode and versionName!
versionName "0.9.7"
versionCode 51
versionName "0.9.8"
versionCode 52
}
buildTypes {
release {

View File

@ -1,15 +1,21 @@
package nodomain.freeyourgadget.gadgetbridge;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.util.TypedValue;
@ -59,6 +65,10 @@ public class GBApplication extends Application {
private static Appender<ILoggingEvent> fileLogger;
private static Prefs prefs;
private static GBPrefs gbPrefs;
/**
* Note: is null on Lollipop and Kitkat
*/
private static NotificationManager notificationManager;
public static final String ACTION_QUIT
= "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit";
@ -119,6 +129,10 @@ public class GBApplication extends Application {
filterLocal.addAction(ACTION_QUIT);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
if (isRunningMarshmallowOrLater()) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
// for testing DB stuff
// SQLiteDatabase db = mActivityDatabaseHandler.getWritableDatabase();
// db.close();
@ -247,6 +261,63 @@ public class GBApplication extends Application {
return VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
public static boolean isRunningMarshmallowOrLater() {
return VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
private static boolean isPrioritySender(int prioritySenders, String number) {
if (prioritySenders == Policy.PRIORITY_SENDERS_ANY) {
return true;
} else {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
String[] projection = new String[]{PhoneLookup._ID, PhoneLookup.STARRED};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
boolean exists = false;
int starred = 0;
try {
if (cursor != null && cursor.moveToFirst()) {
exists = true;
starred = cursor.getInt(cursor.getColumnIndexOrThrow(PhoneLookup.STARRED));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
if (prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS && exists) {
return true;
} else if (prioritySenders == Policy.PRIORITY_SENDERS_STARRED && starred == 1) {
return true;
}
return false;
}
}
@TargetApi(Build.VERSION_CODES.M)
public static boolean isPriorityNumber(int priorityType, String number) {
NotificationManager.Policy notificationPolicy = notificationManager.getNotificationPolicy();
if(priorityType == Policy.PRIORITY_CATEGORY_MESSAGES) {
if ((notificationPolicy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) == Policy.PRIORITY_CATEGORY_MESSAGES) {
return isPrioritySender(notificationPolicy.priorityMessageSenders, number);
}
} else if (priorityType == Policy.PRIORITY_CATEGORY_CALLS) {
if ((notificationPolicy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) == Policy.PRIORITY_CATEGORY_CALLS) {
return isPrioritySender(notificationPolicy.priorityCallSenders, number);
}
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public static int getGrantedInterruptionFilter() {
if (prefs.getBoolean("notification_filter", false) && GBApplication.isRunningMarshmallowOrLater()) {
if (notificationManager.isNotificationPolicyAccessGranted()) {
return notificationManager.getCurrentInterruptionFilter();
}
}
return NotificationManager.INTERRUPTION_FILTER_ALL;
}
public static HashSet<String> blacklist = null;
private static void loadBlackList() {

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -47,7 +48,6 @@ public class AppManagerActivity extends GBActivity {
if (action.equals(GBApplication.ACTION_QUIT)) {
finish();
} else if (action.equals(ACTION_REFRESH_APPLIST)) {
appList.clear();
int appCount = intent.getIntExtra("app_count", 0);
for (Integer i = 0; i < appCount; i++) {
String appName = intent.getStringExtra("app_name" + i.toString());
@ -55,11 +55,21 @@ public class AppManagerActivity extends GBActivity {
UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString()));
GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)];
appList.add(new GBDeviceApp(uuid, appName, appCreator, "", appType));
}
if (prefs.getBoolean("pebble_force_untested", false)) {
appList.addAll(getSystemApps());
boolean found = false;
for (final ListIterator<GBDeviceApp> iter = appList.listIterator(); iter.hasNext(); ) {
final GBDeviceApp app = iter.next();
if (app.getUUID().equals(uuid)) {
app.setOnDevice(true);
iter.set(app);
found = true;
break;
}
}
if (!found) {
GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType);
app.setOnDevice(true);
appList.add(app);
}
}
mGBDeviceAppAdapter.notifyDataSetChanged();
@ -76,8 +86,10 @@ public class AppManagerActivity extends GBActivity {
private List<GBDeviceApp> getSystemApps() {
List<GBDeviceApp> systemApps = new ArrayList<>();
systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
if (prefs.getBoolean("pebble_force_untested", false)) {
systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
}
if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) {
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
}
@ -149,9 +161,7 @@ public class AppManagerActivity extends GBActivity {
appList.addAll(getCachedApps());
if (prefs.getBoolean("pebble_force_untested", false)) {
appList.addAll(getSystemApps());
}
appList.addAll(getSystemApps());
IntentFilter filter = new IntentFilter();
filter.addAction(GBApplication.ACTION_QUIT);
@ -171,11 +181,13 @@ public class AppManagerActivity extends GBActivity {
if (!selectedApp.isInCache()) {
menu.removeItem(R.id.appmanager_app_reinstall);
menu.removeItem(R.id.appmanager_app_delete_cache);
}
if (!PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) {
menu.removeItem(R.id.appmanager_health_activate);
menu.removeItem(R.id.appmanager_health_deactivate);
} else if (PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) {
}
if (selectedApp.getType() == GBDeviceApp.Type.APP_SYSTEM) {
menu.removeItem(R.id.appmanager_app_delete);
}
if (!selectedApp.isConfigurable()) {
@ -184,10 +196,42 @@ public class AppManagerActivity extends GBActivity {
menu.setHeaderTitle(selectedApp.getName());
}
private void removeAppFromList(UUID uuid) {
for (final ListIterator<GBDeviceApp> iter = appList.listIterator(); iter.hasNext(); ) {
final GBDeviceApp app = iter.next();
if (app.getUUID().equals(uuid)) {
iter.remove();
mGBDeviceAppAdapter.notifyDataSetChanged();
break;
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.appmanager_health_deactivate:
case R.id.appmanager_app_delete_cache:
String baseName;
try {
baseName = FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID();
} catch (IOException e) {
LOG.warn("could not get external dir while trying to access pbw cache.");
return true;
}
String[] suffixToDelete = new String[]{".pbw", ".json", "_config.js"};
for (String suffix : suffixToDelete) {
File fileToDelete = new File(baseName + suffix);
if (!fileToDelete.delete()) {
LOG.warn("could not delete file from pbw cache: " + fileToDelete.toString());
} else {
LOG.info("deleted file: " + fileToDelete.toString());
}
}
removeAppFromList(selectedApp.getUUID());
// fall through
case R.id.appmanager_app_delete:
GBApplication.deviceService().onAppDelete(selectedApp.getUUID());
return true;
@ -196,7 +240,7 @@ public class AppManagerActivity extends GBActivity {
try {
cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID() + ".pbw");
} catch (IOException e) {
LOG.warn("could not get external dir while reading pbw cache.");
LOG.warn("could not get external dir while trying to access pbw cache.");
return true;
}
GBApplication.deviceService().onInstallApp(Uri.fromFile(cachePath));

View File

@ -6,6 +6,7 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
@ -107,6 +108,12 @@ public class SettingsActivity extends AbstractSettingsActivity {
});
if (!GBApplication.isRunningMarshmallowOrLater()) {
pref = findPreference("notification_filter");
PreferenceCategory category = (PreferenceCategory) findPreference("pref_key_notifications");
category.removePreference(pref);
}
// Get all receivers of Media Buttons
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);

View File

@ -41,7 +41,15 @@ public class GBDeviceAppAdapter extends ArrayAdapter<GBDeviceApp> {
ImageView deviceImageView = (ImageView) view.findViewById(R.id.item_image);
deviceAppVersionAuthorLabel.setText(getContext().getString(R.string.appversion_by_creator, deviceApp.getVersion(), deviceApp.getCreator()));
deviceAppNameLabel.setText(deviceApp.getName());
// FIXME: replace with small icons
String appNameLabelText = deviceApp.getName();
if (deviceApp.isInCache() || deviceApp.isOnDevice()) {
appNameLabelText += " (" + (deviceApp.isInCache() ? "C" : "")
+ (deviceApp.isOnDevice() ? "D" : "") + ")";
}
deviceAppNameLabel.setText(appNameLabelText);
switch (deviceApp.getType()) {
case APP_GENERIC:
deviceImageView.setImageResource(R.drawable.ic_watchapp);

View File

@ -129,10 +129,6 @@ public class PBWInstallHandler implements InstallHandler {
return;
}
if (!device.getFirmwareVersion().startsWith("v3")) {
return;
}
File destDir;
GBDeviceApp app = mPBWReader.getGBDeviceApp();
try {

View File

@ -0,0 +1,43 @@
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
public class BluetoothConnectReceiver extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
final DeviceCommunicationService service;
public BluetoothConnectReceiver(DeviceCommunicationService service) {
this.service = service;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
return;
}
LOG.info("got connection attempt");
GBDevice gbDevice = service.getGBDevice();
if (gbDevice != null) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getAddress().equals(gbDevice.getAddress())) {
LOG.info("will connect to " + gbDevice.getName());
GBApplication.deviceService().connect();
} else {
LOG.info("won't connect to " + device.getAddress() + "(" + device.getName() + ")");
}
}
}
}

View File

@ -1,5 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -33,6 +34,14 @@ public class K9Receiver extends BroadcastReceiver {
return;
}
}
switch (GBApplication.getGrantedInterruptionFilter()) {
case NotificationManager.INTERRUPTION_FILTER_ALL:
break;
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
case NotificationManager.INTERRUPTION_FILTER_NONE:
case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
return;
}
String uriWanted = intent.getData().toString();

View File

@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -167,6 +168,16 @@ public class NotificationListener extends NotificationListenerService {
return;
}
}
switch (GBApplication.getGrantedInterruptionFilter()) {
case NotificationManager.INTERRUPTION_FILTER_ALL:
break;
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
case NotificationManager.INTERRUPTION_FILTER_NONE:
return;
case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
// FIXME: Handle Reminders and Events if they are enabled in Do Not Disturb
return;
}
String source = sbn.getPackageName();
Notification notification = sbn.getNotification();

View File

@ -1,5 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -67,6 +69,19 @@ public class PhoneCallReceiver extends BroadcastReceiver {
if ("never".equals(prefs.getString("notification_mode_calls", "always"))) {
return;
}
switch (GBApplication.getGrantedInterruptionFilter()) {
case NotificationManager.INTERRUPTION_FILTER_ALL:
break;
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
case NotificationManager.INTERRUPTION_FILTER_NONE:
return;
case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
if (GBApplication.isPriorityNumber(Policy.PRIORITY_CATEGORY_CALLS, mSavedNumber)) {
break;
}
// FIXME: Handle Repeat callers if it is enabled in Do Not Disturb
return;
}
CallSpec callSpec = new CallSpec();
callSpec.number = mSavedNumber;
callSpec.command = callCommand;

View File

@ -1,5 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -41,6 +43,18 @@ public class SMSReceiver extends BroadcastReceiver {
notificationSpec.body = message.getDisplayMessageBody();
notificationSpec.phoneNumber = message.getOriginatingAddress();
if (notificationSpec.phoneNumber != null) {
switch (GBApplication.getGrantedInterruptionFilter()) {
case NotificationManager.INTERRUPTION_FILTER_ALL:
break;
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
case NotificationManager.INTERRUPTION_FILTER_NONE:
return;
case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
if (GBApplication.isPriorityNumber(Policy.PRIORITY_CATEGORY_MESSAGES, notificationSpec.phoneNumber)) {
break;
}
return;
}
GBApplication.deviceService().onNotification(notificationSpec);
}
}

View File

@ -12,6 +12,7 @@ public class GBDeviceApp {
private final UUID uuid;
private final Type type;
private final boolean inCache;
private boolean isOnDevice;
private final boolean configurable;
public GBDeviceApp(UUID uuid, String name, String creator, String version, Type type) {
@ -23,6 +24,7 @@ public class GBDeviceApp {
//FIXME: do not assume
this.inCache = false;
this.configurable = false;
this.isOnDevice = false;
}
public GBDeviceApp(JSONObject json, boolean configurable) {
@ -52,10 +54,18 @@ public class GBDeviceApp {
this.configurable = configurable;
}
public void setOnDevice(boolean isOnDevice) {
this.isOnDevice = isOnDevice;
}
public boolean isInCache() {
return inCache;
}
public boolean isOnDevice() {
return isOnDevice;
}
public String getName() {
return name;
}

View File

@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service;
import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -24,6 +25,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.K9Receiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.PebbleReceiver;
@ -105,6 +107,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
private PebbleReceiver mPebbleReceiver = null;
private MusicPlaybackReceiver mMusicPlaybackReceiver = null;
private TimeChangeReceiver mTimeChangeReceiver = null;
private BluetoothConnectReceiver mBlueToothConnectReceiver = null;
private Random mRandom = new Random();
@ -279,6 +282,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
case ACTION_DISCONNECT: {
mDeviceSupport.dispose();
if (mGBDevice != null && mGBDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
setReceiversEnableState(false);
mGBDevice.setState(GBDevice.State.NOT_CONNECTED);
mGBDevice.sendDeviceUpdateIntent(this);
}
mDeviceSupport = null;
break;
}
@ -457,6 +465,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
filter.addAction("android.intent.action.TIMEZONE_CHANGED");
registerReceiver(mTimeChangeReceiver, filter);
}
if (mBlueToothConnectReceiver == null) {
mBlueToothConnectReceiver = new BluetoothConnectReceiver(this);
registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
}
} else {
if (mPhoneCallReceiver != null) {
unregisterReceiver(mPhoneCallReceiver);
@ -482,6 +494,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
unregisterReceiver(mTimeChangeReceiver);
mTimeChangeReceiver = null;
}
if (mBlueToothConnectReceiver != null) {
unregisterReceiver(mBlueToothConnectReceiver);
mBlueToothConnectReceiver = null;
}
}
}
@ -549,4 +565,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
public GBPrefs getGBPrefs() {
return GBApplication.getGBPrefs();
}
public GBDevice getGBDevice() {
return mGBDevice;
}
}

View File

@ -0,0 +1,99 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
class DatalogSessionHealthOverlayData extends DatalogSession {
private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthOverlayData.class);
public DatalogSessionHealthOverlayData(byte id, UUID uuid, int tag, byte item_type, short item_size) {
super(id, uuid, tag, item_type, item_size);
taginfo = "(health - overlay data " + tag + " )";
}
@Override
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
int initialPosition = datalogMessage.position();
int beginOfRecordPosition;
short recordVersion; //probably
short recordType; //probably: 1=sleep, 2=deep sleep, 5=??run??ignored for now
if (0 != (length % itemSize))
return false;//malformed message?
int recordCount = length / itemSize;
OverlayRecord[] overlayRecords = new OverlayRecord[recordCount];
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record
recordVersion = datalogMessage.getShort();
if ((recordVersion != 1) && (recordVersion != 3))
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
datalogMessage.getShort();//throwaway, unknown
recordType = datalogMessage.getShort();
overlayRecords[recordIdx] = new OverlayRecord(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt());
}
return store(overlayRecords);//NACK if we cannot store the data yet, the watch will send the overlay records again.
}
private boolean store(OverlayRecord[] overlayRecords) {
DBHandler dbHandler = null;
SampleProvider sampleProvider = new HealthSampleProvider();
try {
dbHandler = GBApplication.acquireDB();
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
for (OverlayRecord overlayRecord : overlayRecords) {
if (latestTimestamp < (overlayRecord.timestampStart + overlayRecord.durationSeconds))
return false;
switch (overlayRecord.type) {
case 1:
dbHandler.changeStoredSamplesType(overlayRecord.timestampStart, (overlayRecord.timestampStart + overlayRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
break;
case 2:
dbHandler.changeStoredSamplesType(overlayRecord.timestampStart, (overlayRecord.timestampStart + overlayRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), sampleProvider);
break;
default:
//TODO: other values refer to unknown activity types.
}
}
} catch (Exception ex) {
LOG.debug(ex.getMessage());
} finally {
if (dbHandler != null) {
dbHandler.release();
}
}
return true;
}
private class OverlayRecord {
int type; //1=sleep, 2=deep sleep
int offsetUTC; //probably
int timestampStart;
int durationSeconds;
public OverlayRecord(int type, int offsetUTC, int timestampStart, int durationSeconds) {
this.type = type;
this.offsetUTC = offsetUTC;
this.timestampStart = timestampStart;
this.durationSeconds = durationSeconds;
}
}
}

View File

@ -1,7 +1,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,71 +25,6 @@ class DatalogSessionHealthSleep extends DatalogSession {
@Override
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
switch (this.tag) {
case 83:
return handleMessage83(datalogMessage, length);
case 84:
return handleMessage84(datalogMessage, length);
default:
return false;
}
}
private boolean handleMessage84(ByteBuffer datalogMessage, int length) {
int initialPosition = datalogMessage.position();
int beginOfRecordPosition;
short recordVersion; //probably
short recordType; //probably: 1=sleep, 2=deep sleep
if (0 != (length % itemSize))
return false;//malformed message?
int recordCount = length / itemSize;
SleepRecord84[] sleepRecords = new SleepRecord84[recordCount];
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record
recordVersion = datalogMessage.getShort();
if (recordVersion != 1)
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
datalogMessage.getShort();//throwaway, unknown
recordType = datalogMessage.getShort();
sleepRecords[recordIdx] = new SleepRecord84(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt());
}
return store84(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
}
private boolean store84(SleepRecord84[] sleepRecords) {
DBHandler dbHandler = null;
SampleProvider sampleProvider = new HealthSampleProvider();
try {
dbHandler = GBApplication.acquireDB();
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
for (SleepRecord84 sleepRecord : sleepRecords) {
if (latestTimestamp < (sleepRecord.timestampStart + sleepRecord.durationSeconds))
return false;
if (sleepRecord.type == 2) {
dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), sampleProvider);
} else {
dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
}
}
} catch (Exception ex) {
LOG.debug(ex.getMessage());
} finally {
if (dbHandler != null) {
dbHandler.release();
}
}
return true;
}
private boolean handleMessage83(ByteBuffer datalogMessage, int length) {
int initialPosition = datalogMessage.position();
int beginOfRecordPosition;
short recordVersion; //probably
@ -100,7 +33,7 @@ class DatalogSessionHealthSleep extends DatalogSession {
return false;//malformed message?
int recordCount = length / itemSize;
SleepRecord83[] sleepRecords = new SleepRecord83[recordCount];
SleepRecord[] sleepRecords = new SleepRecord[recordCount];
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
@ -109,23 +42,22 @@ class DatalogSessionHealthSleep extends DatalogSession {
if (recordVersion != 1)
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
sleepRecords[recordIdx] = new SleepRecord83(datalogMessage.getInt(),
sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(),
datalogMessage.getInt(),
datalogMessage.getInt(),
datalogMessage.getInt());
}
return store83(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
}
private boolean store83(SleepRecord83[] sleepRecords) {
private boolean store(SleepRecord[] sleepRecords) {
DBHandler dbHandler = null;
SampleProvider sampleProvider = new HealthSampleProvider();
GB.toast("Deep sleep is supported only from firmware 3.11 onwards.", Toast.LENGTH_LONG, GB.INFO);
try {
dbHandler = GBApplication.acquireDB();
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
for (SleepRecord83 sleepRecord : sleepRecords) {
for (SleepRecord sleepRecord : sleepRecords) {
if (latestTimestamp < sleepRecord.bedTimeEnd)
return false;
dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
@ -140,13 +72,13 @@ class DatalogSessionHealthSleep extends DatalogSession {
return true;
}
private class SleepRecord83 {
private class SleepRecord {
int offsetUTC; //probably
int bedTimeStart;
int bedTimeEnd;
int deepSleepSeconds;
public SleepRecord83(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) {
public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) {
this.offsetUTC = offsetUTC;
this.bedTimeStart = bedTimeStart;
this.bedTimeEnd = bedTimeEnd;
@ -154,17 +86,4 @@ class DatalogSessionHealthSleep extends DatalogSession {
}
}
private class SleepRecord84 {
int type; //1=sleep, 2=deep sleep
int offsetUTC; //probably
int timestampStart;
int durationSeconds;
public SleepRecord84(int type, int offsetUTC, int timestampStart, int durationSeconds) {
this.type = type;
this.offsetUTC = offsetUTC;
this.timestampStart = timestampStart;
this.durationSeconds = durationSeconds;
}
}
}

View File

@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -48,9 +47,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class PebbleIoThread extends GBDeviceIoThread {
private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class);
private static final UUID PEBBLE_UUID_RECONNECT = UUID.fromString("00000000-deca-fade-deca-deafdecacafe");
private static final UUID PEBBLE_UUID_RECONNECT3X = UUID.fromString("a924496e-cc7c-4dff-8a9f-9a76cc2e9d50");
public static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED";
public static final String PEBBLEKIT_ACTION_PEBBLE_DISCONNECTED = "com.getpebble.action.PEBBLE_DISCONNECTED";
public static final String PEBBLEKIT_ACTION_APP_ACK = "com.getpebble.action.app.ACK";
@ -71,7 +67,6 @@ public class PebbleIoThread extends GBDeviceIoThread {
private boolean mIsTCP = false;
private BluetoothAdapter mBtAdapter = null;
private BluetoothSocket mBtSocket = null;
private BluetoothServerSocket mBtServerSocket = null;
private Socket mTCPSocket = null; // for emulator
private InputStream mInStream = null;
private OutputStream mOutStream = null;
@ -365,7 +360,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
LOG.info(e.getMessage());
mIsConnected = false;
int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10);
if (GBApplication.getGBPrefs().getAutoReconnect() && reconnectAttempts > 0) {
if (!mQuit && GBApplication.getGBPrefs().getAutoReconnect() && reconnectAttempts > 0) {
gbDevice.setState(GBDevice.State.CONNECTING);
gbDevice.sendDeviceUpdateIntent(getContext());
int delaySeconds = 1;
@ -383,33 +378,10 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
}
}
if (!mIsConnected && !mQuit) {
try {
gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);
gbDevice.sendDeviceUpdateIntent(getContext());
UUID reconnectUUID = mPebbleProtocol.isFw3x ? PEBBLE_UUID_RECONNECT3X : PEBBLE_UUID_RECONNECT;
mBtServerSocket = mBtAdapter.listenUsingRfcommWithServiceRecord("PebbleReconnectListener", reconnectUUID);
mBtSocket = mBtServerSocket.accept();
LOG.info("incoming connection on reconnect uuid (" + reconnectUUID + "), will connect actively");
mBtSocket.close();
mIsConnected = connect(gbDevice.getAddress());
} catch (IOException ex) {
ex.printStackTrace();
LOG.info("error while reconnecting");
} finally {
try {
if (mBtServerSocket != null) {
mBtServerSocket.close();
mBtServerSocket = null;
}
} catch (IOException ignore) {
}
}
}
if (!mIsConnected) {
mBtSocket = null;
LOG.info("Bluetooth socket closed, will quit IO Thread");
mQuit = true;
break;
}
}
}
@ -421,10 +393,16 @@ public class PebbleIoThread extends GBDeviceIoThread {
} catch (IOException e) {
e.printStackTrace();
}
mBtSocket = null;
}
enablePebbleKitReceiver(false);
mBtSocket = null;
gbDevice.setState(GBDevice.State.NOT_CONNECTED);
if (mQuit) {
gbDevice.setState(GBDevice.State.NOT_CONNECTED);
} else {
gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);
}
gbDevice.sendDeviceUpdateIntent(getContext());
}
@ -700,13 +678,6 @@ public class PebbleIoThread extends GBDeviceIoThread {
e.printStackTrace();
}
}
if (mBtServerSocket != null) {
try {
mBtServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mTCPSocket != null) {
try {
mTCPSocket.close();

View File

@ -1883,8 +1883,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
if (!mDatalogSessions.containsKey(id)) {
if (uuid.equals(UUID_ZERO) && log_tag == 81) {
mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size));
} else if (uuid.equals(UUID_ZERO) && (log_tag == 83 || log_tag == 84)) {
} else if (uuid.equals(UUID_ZERO) && log_tag == 83) {
mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size));
} else if (uuid.equals(UUID_ZERO) && log_tag == 84) {
mDatalogSessions.put(id, new DatalogSessionHealthOverlayData(id, uuid, log_tag, item_type, item_size));
} else {
mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size));
}

View File

@ -78,6 +78,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
private boolean reconnect() {
if (!isConnected() && useAutoConnect()) {
if (getDevice().getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
gbDeviceIOThread.quit();
gbDeviceIOThread.interrupt();
gbDeviceIOThread = null;
if (!connect()) {

View File

@ -6,6 +6,9 @@
<item
android:id="@+id/appmanager_app_delete"
android:title="@string/appmananger_app_delete"/>
<item
android:id="@+id/appmanager_app_delete_cache"
android:title="@string/appmananger_app_delete_cache"/>
<item
android:id="@+id/appmanager_health_activate"
android:title="@string/appmanager_health_activate"/>

View File

@ -14,6 +14,7 @@
<!--Strings related to AppManager-->
<string name="title_activity_appmanager">App Manager</string>
<string name="appmananger_app_delete">Löschen</string>
<string name="appmananger_app_delete_cache">Löschen und aus dem Zwischenspeicher entfernen</string>
<!--Strings related to AppBlacklist-->
<string name="title_activity_appblacklist">Sperre für Benachrichtigungen</string>
<!--Strings related to FwAppInstaller-->
@ -44,6 +45,8 @@
<string name="pref_summary_notifications_pebblemsg">Unterstützung für Anwendungen, die Benachrichtigungen per Intent an die Pebble schicken. Kann für Conversations verwendet werden.</string>
<string name="pref_title_notifications_generic">Andere Benachrichtigungen</string>
<string name="pref_title_whenscreenon">… auch wenn der Bildschirm an ist</string>
<string name="pref_title_notification_filter">Bitte nicht stören</string>
<string name="pref_summary_notification_filter">Stoppe unerwünschte Nachrichten, wenn im \"Nicht Stören\"-Modus</string>
<string name="always">immer</string>
<string name="when_screen_off">wenn der Bildschirm aus ist</string>
<string name="never">niemals</string>
@ -202,8 +205,9 @@
<string name="pref_title_keep_data_on_device">Aktivitätsdaten auf dem Gerät lassen</string>
<string name="miband_fwinstaller_incompatible_version">Inkompatible Firmware</string>
<string name="fwinstaller_firmware_not_compatible_to_device">Diese Firmware ist nicht mit dem Gerät kompatibel</string>
<string name="miband_prefs_reserve_alarm_calendar">Wecker für zukünftige Ereignisse vormerken</string>
<string name="miband_prefs_hr_sleep_detection">Verwende den Herzfrequenzsensor um die Schlaferkennung zu verbessern</string>
<string name="waiting_for_reconnect">warte auf eingehende Verbindung</string>
<string name="waiting_for_reconnect">warte auf Verbindung</string>
<string name="appmananger_app_reinstall">Erneut installieren</string>
<string name="activity_prefs_about_you">Über Dich</string>
<string name="activity_prefs_year_birth">Geburtsjahr</string>
@ -214,6 +218,7 @@
<string name="appmanager_health_deactivate">deaktivieren</string>
<string name="authenticating">Authentifiziere</string>
<string name="authentication_required">Authentifizierung erforderlich</string>
<string name="app_configure">Konfigurieren</string>
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Widget hinzufügen</string>
<string name="activity_prefs_sleep_duration">Gewünschte Schlafdauer in Stunden</string>

View File

@ -69,8 +69,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Notificación de prueba desde Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth no está soportado.</string>
<string name="bluetooth_is_disabled_">Bluetooth está desactivado.</string>
<string name="tap_connected_device_for_app_mananger">pulsa el dispositivo conectado para el Gestor de App</string>
<string name="tap_a_device_to_connect">pulsa un dispositivo para conectar</string>
<string name="cannot_connect_bt_address_invalid_">No se puede conectar. ¿Dirección BT incorrecta?</string>
<string name="gadgetbridge_running">Gadgetbridge funcionando</string>
<string name="installing_binary_d_d">instalando binario %1$d/%2$d</string>
@ -101,13 +99,11 @@
<string name="miband_pairing_using_dummy_userdata">No se han proporcionado datos de usuario válidos, se usarán datos de usuario por defecto.</string>
<string name="miband_pairing_tap_hint">Cuando tu MiBand vibre y parpadee, púlsala repetidas veces.</string>
<string name="appinstaller_install">Instalar</string>
<string name="discovery_connected_devices_hint">Haz visible tu dispositivo. No es probable que se detecten los dispositivos que ya están conectados.</string>
<string name="discovery_note">Nota:</string>
<string name="candidate_item_device_image">Imagen del dispositivo</string>
<string name="miband_prefs_alias">Nombre/Apodo</string>
<string name="pref_header_vibration_count">Número de vibraciones</string>
<string name="title_activity_sleepmonitor">Monitor de sueño</string>
<string name="pref_write_logfiles">Guardar logs</string>
<string name="initializing">iniciando</string>
<string name="busy_task_fetch_activity_data">Recuperando datos de actividad</string>
<string name="sleep_activity_date_range">Desde %1$s a %2$s</string>
@ -178,9 +174,9 @@
<string name="chart_steps">Pasos</string>
<string name="liveactivity_live_activity">Actividad</string>
<string name="weeksteps_today_steps_description">Pasos hoy, objetivo: %1$s</string>
<string name="pref_title_dont_ack_transfer">No confirmar transferencia</string>
<string name="pref_summary_dont_ack_transfers">Si los datos no son marcados como descargados, no serán borrados de tu MiBand. Útil si Gadgetbridge se usa conjuntamente con otras apps.</string>
<string name="pref_summary_keep_data_on_device">Mantendrá los datos de actividad en la MiBand incluso después de la sincronización. Útil si GB se usa junto con otras apps.</string>
<string name="pref_title_dont_ack_transfer">No confirmar transferencia</string>
<string name="live_activity_steps_history">Historial de pasos</string>
<string name="live_activity_current_steps_per_minute">Pasos/min actuales</string>
<string name="live_activity_total_steps">Pasos totales</string>

View File

@ -178,9 +178,9 @@
<string name="chart_steps">Pas</string>
<string name="liveactivity_live_activity">Activité en direct</string>
<string name="weeksteps_today_steps_description">Nombre de pas aujourd\'hui, objectif: %1$s</string>
<string name="pref_title_dont_ack_transfer">Ne pas confirmer le transfert de données d\'activités</string>
<string name="pref_summary_dont_ack_transfers">Les données d\'activités ne seront pas effacées du bracelet si elles ne sont pas confirmées. Utile si GB est utilisé avec d\'autres applications.</string>
<string name="pref_summary_keep_data_on_device">Les données d\'activités seront conservées sur le Mi Band après la synchronisation. Utile si GB est utilisé avec d\'autres applications.</string>
<string name="pref_title_dont_ack_transfer">Ne pas confirmer le transfert de données d\'activités</string>
<string name="live_activity_steps_history">Historique de pas</string>
<string name="live_activity_current_steps_per_minute">Pas/minute actuel</string>
<string name="live_activity_total_steps">Nombre total de pas</string>

View File

@ -70,8 +70,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Notifica di prova creata da Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth non supportato.</string>
<string name="bluetooth_is_disabled_">Bluetooth disabilitato.</string>
<string name="tap_connected_device_for_app_mananger">tocca il dispositivo connesso per gestire le App</string>
<string name="tap_a_device_to_connect">tocca il dispositivo da connettere</string>
<string name="cannot_connect_bt_address_invalid_">Impossibile connettersi. Indirizzo BT non valido?</string>
<string name="gadgetbridge_running">Gadgetbridge in esecuzione</string>
<string name="installing_binary_d_d">installazione del binario %1$d/%2$d</string>
@ -102,13 +100,11 @@
<string name="miband_pairing_using_dummy_userdata">Dati dell\'utente non inseriti, vengono usati dati d\'esempio.</string>
<string name="miband_pairing_tap_hint">Quando la Mi Band vibra e lampeggia, dalle qualche leggero colpetto.</string>
<string name="appinstaller_install">Installa</string>
<string name="discovery_connected_devices_hint">Imposta il tuo dispositivo perchè sia rilevabile. I dispositivi attualmente connessi non saranno probabilmente rilevati.</string>
<string name="discovery_note">Nota:</string>
<string name="candidate_item_device_image">Immagine dispositivo</string>
<string name="miband_prefs_alias">Nome / Soprannome</string>
<string name="pref_header_vibration_count">Numero vibrazioni</string>
<string name="title_activity_sleepmonitor">Monitoraggio del sonno</string>
<string name="pref_write_logfiles">Salva il log su file</string>
<string name="initializing">inizializzazione in corso</string>
<string name="busy_task_fetch_activity_data">Recupero dati attività</string>
<string name="sleep_activity_date_range">Da %1$s a %2$s</string>
@ -179,9 +175,9 @@
<string name="chart_steps">Passi</string>
<string name="liveactivity_live_activity">Attività in tempo reale</string>
<string name="weeksteps_today_steps_description">Passi di oggi, traguardo: %1$s</string>
<string name="pref_title_dont_ack_transfer">Non confermare il trasferimento dati</string>
<string name="pref_summary_dont_ack_transfers">Se il trasferimento non viene confermato, i dati rimangono memorizzati sulla Mi Band. Utile se GB è usato insieme ad altre app.</string>
<string name="pref_summary_keep_data_on_device">Conserva i dati delle attività sulla Mi Band anche dopo averli sincronizzati. Utile se GB è usato insieme ad altre app.</string>
<string name="pref_title_dont_ack_transfer">Non confermare il trasferimento dati</string>
<string name="live_activity_steps_history">Storico dei passi</string>
<string name="live_activity_current_steps_per_minute">Passi/minuto</string>
<string name="live_activity_total_steps">Passi totali</string>

View File

@ -14,6 +14,7 @@
<!--Strings related to AppManager-->
<string name="title_activity_appmanager">アプリ管理画面</string>
<string name="appmananger_app_delete">削除</string>
<string name="appmananger_app_delete_cache">キャッシュから削除</string>
<!--Strings related to AppBlacklist-->
<string name="title_activity_appblacklist">ステータス通知ブラックリスト</string>
<!--Strings related to FwAppInstaller-->
@ -44,6 +45,8 @@
<string name="pref_summary_notifications_pebblemsg">インテント経由でPebbleに通知を送信するアプリケーションをサポートします。Conversationsに使用することができます。</string>
<string name="pref_title_notifications_generic">一般ステータス通知対応</string>
<string name="pref_title_whenscreenon">… スクリーンがオンのときにも</string>
<string name="pref_title_notification_filter">サイレント</string>
<string name="pref_summary_notification_filter">サイレントモードに基づいて、送信される不要な通知を停止します。</string>
<string name="always">いつも</string>
<string name="when_screen_off">スクリーンがオフのとき</string>
<string name="never">なし</string>

View File

@ -181,9 +181,9 @@
<string name="chart_steps">걸음 수</string>
<string name="liveactivity_live_activity">실시간 활동</string>
<string name="weeksteps_today_steps_description">오늘 걸음 수, 목표: %1$s</string>
<string name="pref_title_dont_ack_transfer">활동 데이터 전송을 확인하지 않음</string>
<string name="pref_summary_dont_ack_transfers">만약 활동 데이터가 밴드에 확인되지 않았다면, 지워지지 않을 것입니다. 가젯브릿지가 다른 앱들과 같이 사용될 때 유용합니다.</string>
<string name="pref_summary_keep_data_on_device">동기화 이후에도 Mi Band의 활동 데이터가 유지될 것입니다. 가젯브릿지가 다른 앱들과 같이 사용될 때 유용합니다.</string>
<string name="pref_title_dont_ack_transfer">활동 데이터 전송을 확인하지 않음</string>
<string name="live_activity_steps_history">걸음 수 기록</string>
<string name="live_activity_current_steps_per_minute">현재의 분당 걸음 수</string>
<string name="live_activity_total_steps">전체 걸음 수</string>
@ -224,4 +224,5 @@
<string name="updatefirmwareoperation_firmware_not_sent">펌웨어가 전송되지 않음</string>
<string name="charts_legend_heartrate">심박수</string>
<string name="live_activity_heart_rate">심박수</string>
<string name="pref_title_general_autocreonnect">자동으로 재연결</string>
</resources>

View File

@ -67,8 +67,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">To jest testowe powiadomienie z Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth nie jest obsługiwane</string>
<string name="bluetooth_is_disabled_">Bluetooth jest wyłączone</string>
<string name="tap_connected_device_for_app_mananger">Kliknij podłączone urządzenia dla zarządzania aplikacjami</string>
<string name="tap_a_device_to_connect">kliknij urządzenie by połączyć</string>
<string name="cannot_connect_bt_address_invalid_">Nie można połączyć. Adres BT nieprawidłowy?</string>
<string name="gadgetbridge_running">Gadgetbridge działa</string>
<string name="installing_binary_d_d">Instalowanie binarki %1$d/%2$d</string>
@ -99,13 +97,11 @@
<string name="miband_pairing_using_dummy_userdata">Brak prawidłowych danych użytkownika, używam danych zastępczych na ten moment.</string>
<string name="miband_pairing_tap_hint">Gdy twój Mi Band wibruje i błyska, stuknij go kilka razy pod rząd.</string>
<string name="appinstaller_install">instaluj</string>
<string name="discovery_connected_devices_hint">Uwidocznij swoje urządzenie. Aktualnie połączone urządzenia prawdopodobnie nie będą znalezione.</string>
<string name="discovery_note">Uwaga</string>
<string name="candidate_item_device_image">Obraz urządzenia</string>
<string name="miband_prefs_alias">Nazwisko/Pseudonim</string>
<string name="pref_header_vibration_count">Liczba wibracji</string>
<string name="title_activity_sleepmonitor">Monitor snu</string>
<string name="pref_write_logfiles">Zapisuj logi</string>
<string name="initializing">Uruchamianie</string>
<string name="busy_task_fetch_activity_data">Pobieranie danych aktywności</string>
<string name="sleep_activity_date_range">Od %1$s do %2$s</string>
@ -176,9 +172,9 @@
<string name="chart_steps">Kroki</string>
<string name="liveactivity_live_activity">Ostatnia aktywność</string>
<string name="weeksteps_today_steps_description">Kroków dziś, cel: %1$s</string>
<string name="pref_title_dont_ack_transfer">Nie wysyłaj danych aktywności</string>
<string name="pref_summary_dont_ack_transfers">Gdy dane aktywności nie są przesłane na opaskę, wtedy nie będą usuwane. Przydatne gdy Gadgetbridge jest używany wraz z innymi aplikacjami</string>
<string name="pref_summary_keep_data_on_device">Dane aktywności będą zachowane na Mi Band nawet po synchronizacji. Przydatne gdy Gadgetbridge jest używany z innymi aplikacjami.</string>
<string name="pref_title_dont_ack_transfer">Nie wysyłaj danych aktywności</string>
<string name="live_activity_steps_history">Historia kroków</string>
<string name="live_activity_current_steps_per_minute">Aktualnie kroków/min</string>
<string name="live_activity_total_steps">Kroków łącznie</string>

View File

@ -68,8 +68,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Это тестовое уведомление от Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth не поддерживается.</string>
<string name="bluetooth_is_disabled_">Bluetooth отключён.</string>
<string name="tap_connected_device_for_app_mananger">нажмите на подключённое устройство для App Manager</string>
<string name="tap_a_device_to_connect">нажмите на устройство для соединения</string>
<string name="cannot_connect_bt_address_invalid_">Не удалось соединиться. Неверен адрес BT?</string>
<string name="gadgetbridge_running">Gadgetbridge запущен</string>
<string name="installing_binary_d_d">установки бинарного файла %1$d/%2$d</string>
@ -100,13 +98,11 @@
<string name="miband_pairing_using_dummy_userdata">Не предоставлено действительных данных пользователя. Используются данные по-умолчанию.</string>
<string name="miband_pairing_tap_hint">Когда ваш Mi Band вибрирует и мигает, постучите по нему несколько раз.</string>
<string name="appinstaller_install">Установить</string>
<string name="discovery_connected_devices_hint">Подключённые в настоящее время устройства, скорее всего, не будут обнаружены.</string>
<string name="discovery_note">Заметка:</string>
<string name="candidate_item_device_image">Изображение устройства</string>
<string name="miband_prefs_alias">Имя/псевдоним</string>
<string name="pref_header_vibration_count">Количество вибраций</string>
<string name="title_activity_sleepmonitor">Анализ сна</string>
<string name="pref_write_logfiles">Записывать файлы журнала</string>
<string name="initializing">Инициализация</string>
<string name="busy_task_fetch_activity_data">Получение данных активности</string>
<string name="sleep_activity_date_range">От %1$s до %2$s</string>
@ -177,9 +173,9 @@
<string name="chart_steps">Шаги</string>
<string name="liveactivity_live_activity">Жизненная активность</string>
<string name="weeksteps_today_steps_description">Шагов сегодня, цель: %1$s</string>
<string name="pref_title_dont_ack_transfer">Не передавать данные об активности</string>
<string name="pref_summary_dont_ack_transfers">Если данные об активности не будут переданы на устройство, оно не будет очищено. Полезно, если GB используется с другими приложениями.</string>
<string name="pref_summary_keep_data_on_device">Хранить данные о деятельности на Mi Band, даже после синхронизации. Полезно, если Mi Band используется совместно с другими приложениями.</string>
<string name="pref_title_dont_ack_transfer">Не передавать данные об активности</string>
<string name="live_activity_steps_history">История шагов</string>
<string name="live_activity_current_steps_per_minute">Текущие шаги в минуту</string>
<string name="live_activity_total_steps">Всего шагов</string>

View File

@ -31,6 +31,9 @@
<string name="pref_header_datetime">Дата і час</string>
<string name="pref_title_datetime_syctimeonconnect">Синхронізувати час під час з\'єднання</string>
<string name="pref_summary_datetime_syctimeonconnect">Синхронізувати час під час з\'єднання з пристроєм, а також під час зміни часу чи часової зони в системі</string>
<string name="pref_title_theme">Тема</string>
<string name="pref_theme_light">Світла</string>
<string name="pref_theme_dark">Темна</string>
<string name="pref_header_notifications">Сповіщення</string>
<string name="pref_title_notifications_repetitions">Повтори</string>
<string name="pref_title_notifications_call">Виклики</string>
@ -67,8 +70,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Це тестове сповіщення від Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth не підтримується.</string>
<string name="bluetooth_is_disabled_">Bluetooth вимкнуто.</string>
<string name="tap_connected_device_for_app_mananger">натисніть на під\'єднаний пристрій для App Manager</string>
<string name="tap_a_device_to_connect">натисніть на пристрій для з\'єднання</string>
<string name="cannot_connect_bt_address_invalid_">Не вдалося з\'єднатися. Можливо помилкова адреса BT?</string>
<string name="gadgetbridge_running">Gadgetbridge запущено</string>
<string name="installing_binary_d_d">встановлення бінарного файлу %1$d/%2$d</string>
@ -99,13 +100,11 @@
<string name="miband_pairing_using_dummy_userdata">Не отримано дійсних даних користувача. Використовуються типові дані.</string>
<string name="miband_pairing_tap_hint">Коли ваш Mi—Band вібрує та блимає, постукайте по ньому кілька раз.</string>
<string name="appinstaller_install">Встановити</string>
<string name="discovery_connected_devices_hint">Під\'єднані на даний момент пристрої, скоріш за все не будуть виявлені.</string>
<string name="discovery_note">Замітка:</string>
<string name="candidate_item_device_image">Зображення пристрою</string>
<string name="miband_prefs_alias">Ім\'я/нік</string>
<string name="pref_header_vibration_count">Кількість вібрацій</string>
<string name="title_activity_sleepmonitor">Аналіз сну</string>
<string name="pref_write_logfiles">Записувати файли звіту</string>
<string name="initializing">Ініціалізація…</string>
<string name="busy_task_fetch_activity_data">Отримання даних активності</string>
<string name="sleep_activity_date_range">Від %1$s до %2$s</string>
@ -176,9 +175,9 @@
<string name="chart_steps">Кроки</string>
<string name="liveactivity_live_activity">Життєва активність</string>
<string name="weeksteps_today_steps_description">Кроків сьогодні, мета: %1$s</string>
<string name="pref_title_dont_ack_transfer">Не передавати дані про активність</string>
<string name="pref_summary_dont_ack_transfers">Якщо дані не будуть передані на пристрій, пристрій не буде очищений. Корисно, якщо Gadgetbridge використовується разом з іншими додатками.</string>
<string name="pref_summary_keep_data_on_device">Дозволяє лишити дані на Mi-браслеті після синхронізації. Зазвичай використовується, якщо GB працює ще з іншими додатками.</string>
<string name="pref_title_dont_ack_transfer">Не передавати дані про активність</string>
<string name="live_activity_steps_history">Історія кроків</string>
<string name="live_activity_current_steps_per_minute">Поточні кроки/хв</string>
<string name="live_activity_total_steps">Загалом кроків</string>

View File

@ -52,8 +52,6 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Đây là một kiểm tra thông báo từ Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Không hỗ trợ Bluetooth.</string>
<string name="bluetooth_is_disabled_">Đã tắt Bluetooth.</string>
<string name="tap_connected_device_for_app_mananger">chạm vào thiết bị đã kết nối để chạy Trình quản lý ứng dụng</string>
<string name="tap_a_device_to_connect">chạm vào một thiết bị để kết nối</string>
<string name="cannot_connect_bt_address_invalid_">Không thể kết nối. Địa chỉ BT không hợp lệ?</string>
<string name="gadgetbridge_running">Gadgetbridge đang chạy</string>
<string name="installing_binary_d_d">đang cài phần mềm chạy %1$d/%2$d</string>
@ -83,7 +81,6 @@
<string name="candidate_item_device_image">Ảnh thiết bị</string>
<string name="miband_prefs_alias">Tên/Bí danh</string>
<string name="title_activity_sleepmonitor">Trình giám sát giấc ngủ</string>
<string name="pref_write_logfiles">Ghi tập tin nhật ký</string>
<string name="initializing">đang khởi chạy</string>
<string name="sleep_activity_date_range">Từ %1$s đến %2$s</string>
<string name="miband_prefs_wearside">Đeo bên trái hay phải?</string>

View File

@ -17,6 +17,7 @@
<!-- Strings related to AppManager -->
<string name="title_activity_appmanager">App Manager</string>
<string name="appmananger_app_delete">Delete</string>
<string name="appmananger_app_delete_cache">Delete and remove from cache</string>
<!-- Strings related to AppBlacklist -->
<string name="title_activity_appblacklist">Notification Blacklist</string>
@ -52,6 +53,8 @@
<string name="pref_summary_notifications_pebblemsg">Support for applications which send Notifications to the Pebble via Intent. Can be used for Conversations.</string>
<string name="pref_title_notifications_generic">Generic notification support</string>
<string name="pref_title_whenscreenon">… also when screen is on</string>
<string name="pref_title_notification_filter">Do Not Disturb</string>
<string name="pref_summary_notification_filter">Stop unwanted Notifications from being sent based on the Do Not Disturb mode.</string>
<string name="always">always</string>
<string name="when_screen_off">when screen is off</string>
@ -249,5 +252,4 @@
<string name="charts_legend_heartrate">Heart Rate</string>
<string name="live_activity_heart_rate">Heart Rate</string>
<string name="pref_title_general_autocreonnect">Reconnect automatically</string>
</resources>

View File

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
<release version="0.9.8" versioncode="52">
<change>Pebble: fix more reconnnect issues</change>
<change>Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health</change>
<change>Pebble: option in AppManager to delete files from cache</change>
<change>Pebble: enable pbw cache and watchface configuration for Firmware 2.x</change>
<change>Pebble: allow enabling of Pebble Health without "untested features" being enabled</change>
<change>Honour "Do Not Disturb" for phone calls and SMS</change>
</release>
<release version="0.9.7" versioncode="51">
<change>Pebble: hopefully fix some reconnect issues</change>
<change>Mi Band: fix live activity monitoring running forever if back button pressed</change>

View File

@ -77,6 +77,13 @@
android:defaultValue="false"
android:key="notifications_generic_whenscreenon"
android:title="@string/pref_title_whenscreenon" />
<CheckBoxPreference
android:defaultValue="false"
android:key="notification_filter"
android:summary="@string/pref_summary_notification_filter"
android:title="@string/pref_title_notification_filter" />
<Preference
android:key="pref_key_blacklist"
android:title="@string/pref_blacklist" />