mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-01 05:25:50 +01:00
Merge branch 'master' into background-javascript
This commit is contained in:
commit
86392dbf06
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,8 +1,15 @@
|
||||
###Changelog
|
||||
|
||||
###Version next
|
||||
###Version 0.18.5
|
||||
* Applied some material design guidelines to Charts and (pebble) app management
|
||||
* Pebble: improve compatiblity with watchapp configuration pages
|
||||
* Changed colours: deep sleep is now dark blue, light sleep is now light blue
|
||||
* Support for exporting and importing of preferences in addition to the database
|
||||
* Visual improvements of the pie charts
|
||||
* Add filter by name in the App blacklist activity
|
||||
* Pebble: improve compatibility with watch app configuration pages
|
||||
* Pebble: display battery percentage (will only update once an hour)
|
||||
* HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642)
|
||||
* HPlus: display battery state and warn on low battery
|
||||
|
||||
###Version 0.18.4
|
||||
* Mi Band 2: Display realtime steps in Live Activity
|
||||
|
@ -17,6 +17,7 @@ package nodomain.freeyourgadget.gadgetbridge.daogen;
|
||||
|
||||
import de.greenrobot.daogenerator.DaoGenerator;
|
||||
import de.greenrobot.daogenerator.Entity;
|
||||
import de.greenrobot.daogenerator.Index;
|
||||
import de.greenrobot.daogenerator.Property;
|
||||
import de.greenrobot.daogenerator.Schema;
|
||||
|
||||
@ -39,8 +40,9 @@ public class GBDaoGenerator {
|
||||
private static final String TIMESTAMP_FROM = "timestampFrom";
|
||||
private static final String TIMESTAMP_TO = "timestampTo";
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Schema schema = new Schema(15, MAIN_PACKAGE + ".entities");
|
||||
Schema schema = new Schema(16, MAIN_PACKAGE + ".entities");
|
||||
|
||||
Entity userAttributes = addUserAttributes(schema);
|
||||
Entity user = addUserInfo(schema, userAttributes);
|
||||
@ -63,6 +65,8 @@ public class GBDaoGenerator {
|
||||
addHPlusHealthActivityKindOverlay(schema, user, device);
|
||||
addHPlusHealthActivitySample(schema, user, device);
|
||||
|
||||
addCalendarSyncState(schema, device);
|
||||
|
||||
new DaoGenerator().generateAll(schema, "app/src/main/java");
|
||||
}
|
||||
|
||||
@ -267,6 +271,20 @@ public class GBDaoGenerator {
|
||||
activitySample.addToOne(user, userId);
|
||||
}
|
||||
|
||||
private static void addCalendarSyncState(Schema schema, Entity device) {
|
||||
Entity calendarSyncState = addEntity(schema, "CalendarSyncState");
|
||||
calendarSyncState.addIdProperty();
|
||||
Property deviceId = calendarSyncState.addLongProperty("deviceId").notNull().getProperty();
|
||||
Property calendarEntryId = calendarSyncState.addLongProperty("calendarEntryId").notNull().getProperty();
|
||||
Index indexUnique = new Index();
|
||||
indexUnique.addProperty(deviceId);
|
||||
indexUnique.addProperty(calendarEntryId);
|
||||
indexUnique.makeUnique();
|
||||
calendarSyncState.addIndex(indexUnique);
|
||||
calendarSyncState.addToOne(device, deviceId);
|
||||
calendarSyncState.addIntProperty("hash").notNull();
|
||||
}
|
||||
|
||||
private static Property findProperty(Entity entity, String propertyName) {
|
||||
for (Property prop : entity.getProperties()) {
|
||||
if (propertyName.equals(prop.getPropertyName())) {
|
||||
|
@ -15,7 +15,7 @@ need to create an account and transmit any of your data to the vendor's servers.
|
||||
|
||||
## Supported Devices
|
||||
* Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki section about this device](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
* Pebble 2, Pebble Time 2 (experimental, PAIR WITHIN GADGETBRIDGE) [Wiki section about pebble](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble), most parts apply to Pebble 2 as well
|
||||
* Pebble 2 (add the device from within Gadgetbridge!) [Wiki section about pebble](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble), most parts apply to Pebble 2 as well
|
||||
* Mi Band, Mi Band 1A, Mi Band 1S [Wiki section about this device](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band)
|
||||
* Mi Band 2 [Wiki section about mi band](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band), some parts apply to mi band 2 as well
|
||||
* Vibratissimo (experimental)
|
||||
|
@ -26,8 +26,8 @@ android {
|
||||
targetSdkVersion 25
|
||||
|
||||
// note: always bump BOTH versionCode and versionName!
|
||||
versionName "0.18.4"
|
||||
versionCode 91
|
||||
versionName "0.18.5"
|
||||
versionCode 92
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
@ -53,7 +53,7 @@ android {
|
||||
}
|
||||
|
||||
pmd {
|
||||
toolVersion = '5.5.1'
|
||||
toolVersion = '5.5.5'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -61,7 +61,7 @@ dependencies {
|
||||
// testCompile 'ch.qos.logback:logback-core:1.1.3'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.robolectric:robolectric:3.2.2"
|
||||
testCompile "org.robolectric:robolectric:3.3.2"
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||
@ -69,14 +69,16 @@ dependencies {
|
||||
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||
compile 'com.android.support:support-v4:25.3.1'
|
||||
compile 'com.android.support:design:25.3.1'
|
||||
compile 'com.github.tony19:logback-android-classic:1.1.1-4'
|
||||
compile 'com.github.tony19:logback-android-classic:1.1.1-6'
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
|
||||
compile 'com.github.pfichtner:durationformatter:0.1.1'
|
||||
compile 'de.cketti.library.changelog:ckchangelog:1.2.2'
|
||||
compile 'net.e175.klaus:solarpositioning:0.0.9'
|
||||
compile 'com.github.freeyourgadget:greendao:1998d7cd2d21f662c6044f6ccf3b3a251bbad341'
|
||||
compile 'org.apache.commons:commons-lang3:3.4'
|
||||
// use pristine greendao instead of our custom version, since our custom jitpack-packaged
|
||||
// version contains way too much and our custom patches are in the generator only.
|
||||
compile 'org.greenrobot:greendao:2.2.1'
|
||||
compile 'org.apache.commons:commons-lang3:3.5'
|
||||
|
||||
// compile project(":DaoCore")
|
||||
}
|
||||
|
@ -251,6 +251,7 @@
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".service.NotificationCollectorMonitorService" />
|
||||
<service android:name=".service.DeviceCommunicationService" />
|
||||
|
||||
<receiver
|
||||
|
@ -54,6 +54,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@ -150,6 +151,8 @@ public class GBApplication extends Application {
|
||||
|
||||
if (isRunningMarshmallowOrLater()) {
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
//the following will ensure the notification manager is kept alive
|
||||
startService(new Intent(this, NotificationCollectorMonitorService.class));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,7 @@ import android.os.Bundle;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.TimePicker;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -36,16 +35,15 @@ public class AlarmDetails extends GBActivity {
|
||||
|
||||
private GBAlarm alarm;
|
||||
private TimePicker timePicker;
|
||||
private CheckBox cbSmartWakeup;
|
||||
private CheckBox cbMonday;
|
||||
private CheckBox cbTuesday;
|
||||
private CheckBox cbWednesday;
|
||||
private CheckBox cbThursday;
|
||||
private CheckBox cbFriday;
|
||||
private CheckBox cbSaturday;
|
||||
private CheckBox cbSunday;
|
||||
private CheckedTextView cbSmartWakeup;
|
||||
private CheckedTextView cbMonday;
|
||||
private CheckedTextView cbTuesday;
|
||||
private CheckedTextView cbWednesday;
|
||||
private CheckedTextView cbThursday;
|
||||
private CheckedTextView cbFriday;
|
||||
private CheckedTextView cbSaturday;
|
||||
private CheckedTextView cbSunday;
|
||||
private GBDevice device;
|
||||
private TextView smartAlarmLabel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -56,15 +54,56 @@ public class AlarmDetails extends GBActivity {
|
||||
device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
|
||||
timePicker = (TimePicker) findViewById(R.id.alarm_time_picker);
|
||||
smartAlarmLabel = (TextView) findViewById(R.id.alarm_label_smart_wakeup);
|
||||
cbSmartWakeup = (CheckBox) findViewById(R.id.alarm_cb_smart_wakeup);
|
||||
cbMonday = (CheckBox) findViewById(R.id.alarm_cb_mon);
|
||||
cbTuesday = (CheckBox) findViewById(R.id.alarm_cb_tue);
|
||||
cbWednesday = (CheckBox) findViewById(R.id.alarm_cb_wed);
|
||||
cbThursday = (CheckBox) findViewById(R.id.alarm_cb_thu);
|
||||
cbFriday = (CheckBox) findViewById(R.id.alarm_cb_fri);
|
||||
cbSaturday = (CheckBox) findViewById(R.id.alarm_cb_sat);
|
||||
cbSunday = (CheckBox) findViewById(R.id.alarm_cb_sun);
|
||||
cbSmartWakeup = (CheckedTextView) findViewById(R.id.alarm_cb_smart_wakeup);
|
||||
cbMonday = (CheckedTextView) findViewById(R.id.alarm_cb_monday);
|
||||
cbTuesday = (CheckedTextView) findViewById(R.id.alarm_cb_tuesday);
|
||||
cbWednesday = (CheckedTextView) findViewById(R.id.alarm_cb_wednesday);
|
||||
cbThursday = (CheckedTextView) findViewById(R.id.alarm_cb_thursday);
|
||||
cbFriday = (CheckedTextView) findViewById(R.id.alarm_cb_friday);
|
||||
cbSaturday = (CheckedTextView) findViewById(R.id.alarm_cb_saturday);
|
||||
cbSunday = (CheckedTextView) findViewById(R.id.alarm_cb_sunday);
|
||||
|
||||
|
||||
cbSmartWakeup.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbMonday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbTuesday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbWednesday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbThursday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbFriday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbSaturday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
cbSunday.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
((CheckedTextView) v).toggle();
|
||||
}
|
||||
});
|
||||
|
||||
timePicker.setIs24HourView(DateFormat.is24HourFormat(GBApplication.getContext()));
|
||||
timePicker.setCurrentHour(alarm.getHour());
|
||||
@ -73,7 +112,6 @@ public class AlarmDetails extends GBActivity {
|
||||
cbSmartWakeup.setChecked(alarm.isSmartWakeup());
|
||||
int smartAlarmVisibility = supportsSmartWakeup() ? View.VISIBLE : View.GONE;
|
||||
cbSmartWakeup.setVisibility(smartAlarmVisibility);
|
||||
smartAlarmLabel.setVisibility(smartAlarmVisibility);
|
||||
|
||||
cbMonday.setChecked(alarm.getRepetition(GBAlarm.ALARM_MON));
|
||||
cbTuesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_TUE));
|
||||
|
@ -22,36 +22,29 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter;
|
||||
|
||||
|
||||
public class AppBlacklistActivity extends GBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class);
|
||||
|
||||
private AppBlacklistAdapter appBlacklistAdapter;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@ -68,77 +61,31 @@ public class AppBlacklistActivity extends GBActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_appblacklist);
|
||||
RecyclerView appListView = (RecyclerView) findViewById(R.id.appListView);
|
||||
appListView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
final PackageManager pm = getPackageManager();
|
||||
appBlacklistAdapter = new AppBlacklistAdapter(R.layout.item_app_blacklist, this);
|
||||
|
||||
final List<ApplicationInfo> packageList = pm.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
ListView appListView = (ListView) findViewById(R.id.appListView);
|
||||
appListView.setAdapter(appBlacklistAdapter);
|
||||
|
||||
// sort the package list by label and blacklist status
|
||||
nameMap = new IdentityHashMap<>(packageList.size());
|
||||
for (ApplicationInfo ai : packageList) {
|
||||
CharSequence name = pm.getApplicationLabel(ai);
|
||||
if (name == null) {
|
||||
name = ai.packageName;
|
||||
}
|
||||
if (GBApplication.blacklist.contains(ai.packageName)) {
|
||||
// sort blacklisted first by prefixing with a '!'
|
||||
name = "!" + name;
|
||||
}
|
||||
nameMap.put(ai, name.toString());
|
||||
}
|
||||
|
||||
Collections.sort(packageList, new Comparator<ApplicationInfo>() {
|
||||
SearchView searchView = (SearchView) findViewById(R.id.appListViewSearch);
|
||||
searchView.setIconifiedByDefault(false);
|
||||
searchView.setIconified(false);
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public int compare(ApplicationInfo ai1, ApplicationInfo ai2) {
|
||||
final String s1 = nameMap.get(ai1);
|
||||
final String s2 = nameMap.get(ai2);
|
||||
return s1.compareTo(s2);
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
final ArrayAdapter<ApplicationInfo> adapter = new ArrayAdapter<ApplicationInfo>(this, R.layout.item_with_checkbox, packageList) {
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = inflater.inflate(R.layout.item_with_checkbox, parent, false);
|
||||
}
|
||||
|
||||
ApplicationInfo appInfo = packageList.get(position);
|
||||
TextView deviceAppVersionAuthorLabel = (TextView) view.findViewById(R.id.item_details);
|
||||
TextView deviceAppNameLabel = (TextView) view.findViewById(R.id.item_name);
|
||||
ImageView deviceImageView = (ImageView) view.findViewById(R.id.item_image);
|
||||
CheckBox checkbox = (CheckBox) view.findViewById(R.id.item_checkbox);
|
||||
|
||||
deviceAppVersionAuthorLabel.setText(appInfo.packageName);
|
||||
deviceAppNameLabel.setText(nameMap.get(appInfo));
|
||||
deviceImageView.setImageDrawable(appInfo.loadIcon(pm));
|
||||
|
||||
checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName));
|
||||
|
||||
return view;
|
||||
}
|
||||
};
|
||||
appListView.setAdapter(adapter);
|
||||
|
||||
appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView parent, View v, int position, long id) {
|
||||
String packageName = packageList.get(position).packageName;
|
||||
CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox));
|
||||
checkBox.toggle();
|
||||
if (checkBox.isChecked()) {
|
||||
GBApplication.addToBlacklist(packageName);
|
||||
} else {
|
||||
GBApplication.removeFromBlacklist(packageName);
|
||||
}
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
appBlacklistAdapter.getFilter().filter(newText);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(GBApplication.ACTION_QUIT);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
||||
}
|
||||
|
||||
|
@ -584,7 +584,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
|
||||
startActivity(intent);
|
||||
} else {
|
||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
||||
int bondingStyle = coordinator.getBondingStyle(deviceCandidate);
|
||||
int bondingStyle = coordinator.getBondingStyle(device);
|
||||
if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) {
|
||||
LOG.info("No bonding needed, according to coordinator, so connecting right away");
|
||||
connectAndFinish(device);
|
||||
|
@ -57,7 +57,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton;
|
||||
|
||||
|
||||
public abstract class AbstractAppManagerFragment extends Fragment {
|
||||
@ -277,7 +276,7 @@ public abstract class AbstractAppManagerFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
appListView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_with_details_and_drag_handle, this);
|
||||
mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_pebble_watchapp, this);
|
||||
appListView.setAdapter(mGBDeviceAppAdapter);
|
||||
|
||||
ItemTouchHelper.Callback appItemTouchHelperCallback = new AppItemTouchHelperCallback(mGBDeviceAppAdapter);
|
||||
|
@ -176,14 +176,11 @@ public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
|
||||
|
||||
static synchronized void rewriteAppOrderFile(String filename, List<UUID> uuids) {
|
||||
try {
|
||||
FileWriter fileWriter = new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename);
|
||||
BufferedWriter out = new BufferedWriter(fileWriter);
|
||||
try (BufferedWriter out = new BufferedWriter(new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename))) {
|
||||
for (UUID uuid : uuids) {
|
||||
out.write(uuid.toString());
|
||||
out.newLine();
|
||||
}
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
LOG.warn("can't write app order to file!");
|
||||
}
|
||||
@ -199,9 +196,7 @@ public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
|
||||
static synchronized ArrayList<UUID> getUuidsFromFile(String filename) {
|
||||
ArrayList<UUID> uuids = new ArrayList<>();
|
||||
try {
|
||||
FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename);
|
||||
BufferedReader in = new BufferedReader(fileReader);
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(FileUtils.getExternalFilesDir() + "/" + filename))) {
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
uuids.add(UUID.fromString(line));
|
||||
|
@ -93,7 +93,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
* shift the date by one day.
|
||||
*/
|
||||
public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
protected final int ANIM_TIME = 350;
|
||||
protected final int ANIM_TIME = 250;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractChartFragment.class);
|
||||
|
||||
|
@ -115,6 +115,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
|
||||
|
||||
BarData barData = new BarData(set);
|
||||
barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false);
|
||||
barData.setValueTextSize(10f);
|
||||
|
||||
LimitLine target = new LimitLine(mTargetValue);
|
||||
barChart.getAxisLeft().removeAllLimitLines();
|
||||
|
@ -44,7 +44,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
|
||||
|
||||
@Override
|
||||
String getPieDescription(int targetValue) {
|
||||
return getString(R.string.weeksleepchart_today_sleep_description, DateTimeUtils.minutesToHHMM(targetValue));
|
||||
return getString(R.string.weeksleepchart_today_sleep_description, DateTimeUtils.formatDurationHoursMinutes(targetValue, TimeUnit.MINUTES));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,173 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapter.AppBLViewHolder> implements Filterable {
|
||||
|
||||
private List<ApplicationInfo> applicationInfoList;
|
||||
private final int mLayoutId;
|
||||
private final Context mContext;
|
||||
private final PackageManager mPm;
|
||||
private final IdentityHashMap<ApplicationInfo, String> mNameMap;
|
||||
|
||||
private ApplicationFilter applicationFilter;
|
||||
|
||||
public AppBlacklistAdapter(int layoutId, Context context) {
|
||||
mLayoutId = layoutId;
|
||||
mContext = context;
|
||||
mPm = context.getPackageManager();
|
||||
|
||||
applicationInfoList = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
|
||||
// sort the package list by label and blacklist status
|
||||
mNameMap = new IdentityHashMap<ApplicationInfo, String>(applicationInfoList.size());
|
||||
for (ApplicationInfo ai : applicationInfoList) {
|
||||
CharSequence name = mPm.getApplicationLabel(ai);
|
||||
if (name == null) {
|
||||
name = ai.packageName;
|
||||
}
|
||||
if (GBApplication.blacklist.contains(ai.packageName)) {
|
||||
// sort blacklisted first by prefixing with a '!'
|
||||
name = "!" + name;
|
||||
}
|
||||
mNameMap.put(ai, name.toString());
|
||||
}
|
||||
|
||||
Collections.sort(applicationInfoList, new Comparator<ApplicationInfo>() {
|
||||
@Override
|
||||
public int compare(ApplicationInfo ai1, ApplicationInfo ai2) {
|
||||
final String s1 = mNameMap.get(ai1);
|
||||
final String s2 = mNameMap.get(ai2);
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppBlacklistAdapter.AppBLViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(mContext).inflate(mLayoutId, parent, false);
|
||||
return new AppBLViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(AppBlacklistAdapter.AppBLViewHolder holder, int position) {
|
||||
final ApplicationInfo appInfo = applicationInfoList.get(position);
|
||||
|
||||
holder.deviceAppVersionAuthorLabel.setText(appInfo.packageName);
|
||||
holder.deviceAppNameLabel.setText(mNameMap.get(appInfo));
|
||||
holder.deviceImageView.setImageDrawable(appInfo.loadIcon(mPm));
|
||||
|
||||
holder.checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName));
|
||||
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox));
|
||||
checkBox.toggle();
|
||||
if (checkBox.isChecked()) {
|
||||
GBApplication.addToBlacklist(appInfo.packageName);
|
||||
} else {
|
||||
GBApplication.removeFromBlacklist(appInfo.packageName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return applicationInfoList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
if (applicationFilter == null)
|
||||
applicationFilter = new ApplicationFilter(this, applicationInfoList);
|
||||
return applicationFilter;
|
||||
}
|
||||
|
||||
public class AppBLViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
final CheckBox checkbox;
|
||||
final ImageView deviceImageView;
|
||||
final TextView deviceAppVersionAuthorLabel;
|
||||
final TextView deviceAppNameLabel;
|
||||
|
||||
AppBLViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
checkbox = (CheckBox) itemView.findViewById(R.id.item_checkbox);
|
||||
deviceImageView = (ImageView) itemView.findViewById(R.id.item_image);
|
||||
deviceAppVersionAuthorLabel = (TextView) itemView.findViewById(R.id.item_details);
|
||||
deviceAppNameLabel = (TextView) itemView.findViewById(R.id.item_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ApplicationFilter extends Filter {
|
||||
|
||||
private final AppBlacklistAdapter adapter;
|
||||
private final List<ApplicationInfo> originalList;
|
||||
private final List<ApplicationInfo> filteredList;
|
||||
|
||||
private ApplicationFilter(AppBlacklistAdapter adapter, List<ApplicationInfo> originalList) {
|
||||
super();
|
||||
this.originalList = new ArrayList<>(originalList);
|
||||
this.filteredList = new ArrayList<>();
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterResults performFiltering(CharSequence filter) {
|
||||
filteredList.clear();
|
||||
final Filter.FilterResults results = new Filter.FilterResults();
|
||||
|
||||
if (filter == null || filter.length() == 0)
|
||||
filteredList.addAll(originalList);
|
||||
else {
|
||||
final String filterPattern = filter.toString().toLowerCase().trim();
|
||||
|
||||
for (ApplicationInfo ai : originalList) {
|
||||
CharSequence name = mPm.getApplicationLabel(ai);
|
||||
if (((String) name).contains(filterPattern) ||
|
||||
(ai.packageName.contains(filterPattern))) {
|
||||
filteredList.add(ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.values = filteredList;
|
||||
results.count = filteredList.size();
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence charSequence, Filter.FilterResults filterResults) {
|
||||
adapter.applicationInfoList.clear();
|
||||
adapter.applicationInfoList.addAll((List<ApplicationInfo>) filterResults.values);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -93,7 +93,7 @@ public class GBAlarmListAdapter extends RecyclerView.Adapter<GBAlarmListAdapter.
|
||||
|
||||
@Override
|
||||
public GBAlarmListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.alarm_item, parent, false);
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_alarm, parent, false);
|
||||
ViewHolder vh = new ViewHolder(view);
|
||||
return vh;
|
||||
}
|
||||
|
@ -384,7 +384,9 @@ public class DBHelper {
|
||||
} else {
|
||||
ensureDeviceUpToDate(device, gbDevice, session);
|
||||
}
|
||||
ensureDeviceAttributes(device, gbDevice, session);
|
||||
if (gbDevice.isInitialized()) {
|
||||
ensureDeviceAttributes(device, gbDevice, session);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDeviceCandidate deviceCandidate) {
|
||||
public int getBondingStyle(GBDevice device) {
|
||||
return BONDING_STYLE_ASK;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import android.bluetooth.le.ScanFilter;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@ -233,7 +232,14 @@ public interface DeviceCoordinator {
|
||||
|
||||
/**
|
||||
* Returns how/if the given device should be bonded before connecting to it.
|
||||
* @param deviceCandidate
|
||||
* @param device
|
||||
*/
|
||||
int getBondingStyle(GBDeviceCandidate deviceCandidate);
|
||||
int getBondingStyle(GBDevice device);
|
||||
|
||||
/**
|
||||
* Indicates whether the device has some kind of calender we can sync to.
|
||||
* Also used for generated sunrise/sunset events
|
||||
*/
|
||||
boolean supportsCalendarEvents();
|
||||
|
||||
}
|
||||
|
@ -176,4 +176,9 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -133,57 +133,57 @@ public final class HPlusConstants {
|
||||
public static final Map<Character, Byte> transliterateMap = new HashMap<Character, Byte>(){
|
||||
{
|
||||
//These are missing
|
||||
put('ó', new Byte((byte) 111));
|
||||
put('Ó', new Byte((byte) 79));
|
||||
put('í', new Byte((byte) 105));
|
||||
put('Í', new Byte((byte) 73));
|
||||
put('ú', new Byte((byte) 117));
|
||||
put('Ú', new Byte((byte) 85));
|
||||
put('ó', (byte) 111);
|
||||
put('Ó', (byte) 79);
|
||||
put('í', (byte) 105);
|
||||
put('Í', (byte) 73);
|
||||
put('ú', (byte) 117);
|
||||
put('Ú', (byte) 85);
|
||||
|
||||
//These mostly belong to the extended ASCII table
|
||||
put('Ç', new Byte((byte) 128));
|
||||
put('ü', new Byte((byte) 129));
|
||||
put('é', new Byte((byte) 130));
|
||||
put('â', new Byte((byte) 131));
|
||||
put('ä', new Byte((byte) 132));
|
||||
put('à', new Byte((byte) 133));
|
||||
put('ã', new Byte((byte) 134));
|
||||
put('ç', new Byte((byte) 135));
|
||||
put('ê', new Byte((byte) 136));
|
||||
put('ë', new Byte((byte) 137));
|
||||
put('è', new Byte((byte) 138));
|
||||
put('Ï', new Byte((byte) 139));
|
||||
put('Î', new Byte((byte) 140));
|
||||
put('Ì', new Byte((byte) 141));
|
||||
put('Ã', new Byte((byte) 142));
|
||||
put('Ä', new Byte((byte) 143));
|
||||
put('É', new Byte((byte) 144));
|
||||
put('æ', new Byte((byte) 145));
|
||||
put('Æ', new Byte((byte) 146));
|
||||
put('ô', new Byte((byte) 147));
|
||||
put('ö', new Byte((byte) 148));
|
||||
put('ò', new Byte((byte) 149));
|
||||
put('û', new Byte((byte) 150));
|
||||
put('ù', new Byte((byte) 151));
|
||||
put('ÿ', new Byte((byte) 152));
|
||||
put('Ö', new Byte((byte) 153));
|
||||
put('Ü', new Byte((byte) 154));
|
||||
put('¢', new Byte((byte) 155));
|
||||
put('£', new Byte((byte) 156));
|
||||
put('¥', new Byte((byte) 157));
|
||||
put('ƒ', new Byte((byte) 159));
|
||||
put('á', new Byte((byte) 160));
|
||||
put('ñ', new Byte((byte) 164));
|
||||
put('Ñ', new Byte((byte) 165));
|
||||
put('ª', new Byte((byte) 166));
|
||||
put('º', new Byte((byte) 167));
|
||||
put('¿', new Byte((byte) 168));
|
||||
put('¬', new Byte((byte) 170));
|
||||
put('½', new Byte((byte) 171));
|
||||
put('¼', new Byte((byte) 172));
|
||||
put('¡', new Byte((byte) 173));
|
||||
put('«', new Byte((byte) 174));
|
||||
put('»', new Byte((byte) 175));
|
||||
put('Ç', (byte) 128);
|
||||
put('ü', (byte) 129);
|
||||
put('é', (byte) 130);
|
||||
put('â', (byte) 131);
|
||||
put('ä', (byte) 132);
|
||||
put('à', (byte) 133);
|
||||
put('ã', (byte) 134);
|
||||
put('ç', (byte) 135);
|
||||
put('ê', (byte) 136);
|
||||
put('ë', (byte) 137);
|
||||
put('Ï', (byte) 139);
|
||||
put('è', (byte) 138);
|
||||
put('Î', (byte) 140);
|
||||
put('Ì', (byte) 141);
|
||||
put('Ã', (byte) 142);
|
||||
put('Ä', (byte) 143);
|
||||
put('É', (byte) 144);
|
||||
put('æ', (byte) 145);
|
||||
put('Æ', (byte) 146);
|
||||
put('ô', (byte) 147);
|
||||
put('ö', (byte) 148);
|
||||
put('ò', (byte) 149);
|
||||
put('û', (byte) 150);
|
||||
put('ù', (byte) 151);
|
||||
put('ÿ', (byte) 152);
|
||||
put('Ö', (byte) 153);
|
||||
put('Ü', (byte) 154);
|
||||
put('¢', (byte) 155);
|
||||
put('£', (byte) 156);
|
||||
put('¥', (byte) 157);
|
||||
put('ƒ', (byte) 159);
|
||||
put('á', (byte) 160);
|
||||
put('ñ', (byte) 164);
|
||||
put('Ñ', (byte) 165);
|
||||
put('ª', (byte) 166);
|
||||
put('º', (byte) 167);
|
||||
put('¿', (byte) 168);
|
||||
put('¬', (byte) 170);
|
||||
put('½', (byte) 171);
|
||||
put('¼', (byte) 172);
|
||||
put('¡', (byte) 173);
|
||||
put('«', (byte) 174);
|
||||
put('»', (byte) 175);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -29,6 +29,13 @@ import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
@ -47,13 +54,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext;
|
||||
|
||||
public class HPlusCoordinator extends AbstractDeviceCoordinator {
|
||||
@ -80,6 +80,16 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDevice deviceCandidate) {
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.HPLUS;
|
||||
|
@ -120,6 +120,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
// nothing to delete, yet
|
||||
|
@ -171,6 +171,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasValidUserInfo() {
|
||||
String dummyMacAddress = MiBandService.MAC_ADDRESS_FILTER_1_1A + ":00:00:00";
|
||||
try {
|
||||
|
@ -20,6 +20,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -46,6 +47,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
||||
public PebbleCoordinator() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
String name = candidate.getDevice().getName();
|
||||
@ -71,7 +73,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(GBDevice gbDevice, Device device, DaoSession session) throws GBException {
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
Long deviceId = device.getId();
|
||||
QueryBuilder<?> qb = session.getPebbleHealthActivitySampleDao().queryBuilder();
|
||||
qb.where(PebbleHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
@ -155,6 +157,11 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
||||
return AppManagerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsBackgroundWebView(GBDevice device) {
|
||||
return true;
|
||||
|
@ -120,6 +120,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
// nothing to delete, yet
|
||||
|
@ -0,0 +1,72 @@
|
||||
/* Copyright (C) 2015-2017 João Paulo Barraca
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.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.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
|
||||
/**
|
||||
* Created by jpbarraca on 13/04/2017.
|
||||
*/
|
||||
|
||||
public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
|
||||
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class);
|
||||
|
||||
final DeviceCommunicationService service;
|
||||
|
||||
public BluetoothPairingRequestReceiver(DeviceCommunicationService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
|
||||
if (!action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GBDevice gbDevice = service.getGBDevice();
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
if (gbDevice == null || device == null)
|
||||
return;
|
||||
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
||||
try {
|
||||
if (coordinator.getBondingStyle(gbDevice) == DeviceCoordinator.BONDING_STYLE_NONE) {
|
||||
LOG.info("Aborting unwanted pairing request");
|
||||
abortBroadcast();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Could not abort pairing request process");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.externalevents;
|
||||
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Enumeration;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class CalendarReceiver extends BroadcastReceiver {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CalendarReceiver.class);
|
||||
private Hashtable<Long, EventSyncState> eventState = new Hashtable<>();
|
||||
|
||||
private GBDevice mGBDevice;
|
||||
|
||||
private class EventSyncState {
|
||||
private int state;
|
||||
private CalendarEvents.CalendarEvent event;
|
||||
|
||||
EventSyncState(CalendarEvents.CalendarEvent event, int state) {
|
||||
this.state = state;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public CalendarEvents.CalendarEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(CalendarEvents.CalendarEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EventState {
|
||||
private static final int NOT_SYNCED = 0;
|
||||
private static final int SYNCED = 1;
|
||||
private static final int NEEDS_UPDATE = 2;
|
||||
private static final int NEEDS_DELETE = 3;
|
||||
}
|
||||
|
||||
public CalendarReceiver(GBDevice gbDevice) {
|
||||
LOG.info("Created calendar receiver.");
|
||||
mGBDevice = gbDevice;
|
||||
syncCalendar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
LOG.info("got calendar changed broadcast");
|
||||
syncCalendar();
|
||||
}
|
||||
|
||||
public void syncCalendar() {
|
||||
LOG.info("Syncing with calendar.");
|
||||
List<CalendarEvents.CalendarEvent> eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext());
|
||||
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
|
||||
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
DaoSession session = dbHandler.getDaoSession();
|
||||
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
|
||||
|
||||
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
|
||||
|
||||
|
||||
for (CalendarEvents.CalendarEvent e : eventList) {
|
||||
eventTable.put(e.getId(), e);
|
||||
if (!eventState.containsKey(e.getId())) {
|
||||
qb = session.getCalendarSyncStateDao().queryBuilder();
|
||||
|
||||
CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(e.getId())))
|
||||
.build().unique();
|
||||
if (calendarSyncState == null) {
|
||||
eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED));
|
||||
} else if (calendarSyncState.getHash() == e.hashCode()) {
|
||||
eventState.put(e.getId(), new EventSyncState(e, EventState.NEEDS_UPDATE));
|
||||
} else {
|
||||
eventState.put(e.getId(), new EventSyncState(e, EventState.SYNCED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add all missing calendar ids on the device to sync status (so that they are deleted later)
|
||||
List<CalendarSyncState> CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list();
|
||||
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
|
||||
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
|
||||
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
|
||||
LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<Long> ids = eventState.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
qb = session.getCalendarSyncStateDao().queryBuilder();
|
||||
Long i = ids.nextElement();
|
||||
EventSyncState es = eventState.get(i);
|
||||
if (eventTable.containsKey(i)) {
|
||||
if (es.getState() == EventState.SYNCED) {
|
||||
if (!es.getEvent().equals(eventTable.get(i))) {
|
||||
eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (es.getState() == EventState.NOT_SYNCED) {
|
||||
// delete for current device only
|
||||
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
|
||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
eventState.remove(i);
|
||||
} else {
|
||||
es.setState(EventState.NEEDS_DELETE);
|
||||
eventState.put(i, es);
|
||||
}
|
||||
}
|
||||
updateEvents(deviceId, session);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GB.toast("Database Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateEvents(Long deviceId, DaoSession session) {
|
||||
Enumeration<Long> ids = eventState.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
Long i = ids.nextElement();
|
||||
EventSyncState es = eventState.get(i);
|
||||
int syncState = es.getState();
|
||||
if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) {
|
||||
CalendarEvents.CalendarEvent calendarEvent = es.getEvent();
|
||||
CalendarEventSpec calendarEventSpec = new CalendarEventSpec();
|
||||
calendarEventSpec.id = i;
|
||||
calendarEventSpec.title = calendarEvent.getTitle();
|
||||
calendarEventSpec.allDay = calendarEvent.isAllDay();
|
||||
calendarEventSpec.timestamp = calendarEvent.getBeginSeconds();
|
||||
calendarEventSpec.durationInSeconds = calendarEvent.getDurationSeconds(); //FIXME: leads to problems right now
|
||||
if (calendarEvent.isAllDay()) {
|
||||
//force the all day events to begin at midnight and last a whole day
|
||||
Calendar c = GregorianCalendar.getInstance();
|
||||
c.setTimeInMillis(calendarEvent.getBegin());
|
||||
c.set(Calendar.HOUR, 0);
|
||||
calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000);
|
||||
calendarEventSpec.durationInSeconds = 24 * 60 * 60; //TODO: commented because it is commented above
|
||||
}
|
||||
calendarEventSpec.description = calendarEvent.getDescription();
|
||||
calendarEventSpec.location = calendarEvent.getLocation();
|
||||
calendarEventSpec.type = CalendarEventSpec.TYPE_UNKNOWN;
|
||||
if (syncState == EventState.NEEDS_UPDATE) {
|
||||
GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i);
|
||||
}
|
||||
GBApplication.deviceService().onAddCalendarEvent(calendarEventSpec);
|
||||
es.setState(EventState.SYNCED);
|
||||
eventState.put(i, es);
|
||||
// update db
|
||||
session.insertOrReplace(new CalendarSyncState(null, deviceId, i, es.event.hashCode()));
|
||||
} else if (syncState == EventState.NEEDS_DELETE) {
|
||||
GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i);
|
||||
eventState.remove(i);
|
||||
// delete from db for current device only
|
||||
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
|
||||
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
|
||||
.buildDelete().executeDeleteWithoutDetachingEntities();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -105,10 +105,10 @@ public class GBDeviceService implements DeviceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(@Nullable GBDevice device, boolean performPair) {
|
||||
public void connect(@Nullable GBDevice device, boolean firstTime) {
|
||||
Intent intent = createIntent().setAction(ACTION_CONNECT)
|
||||
.putExtra(GBDevice.EXTRA_DEVICE, device)
|
||||
.putExtra(EXTRA_PERFORM_PAIR, performPair);
|
||||
.putExtra(EXTRA_CONNECT_FIRST_TIME, firstTime);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
|
@ -27,4 +27,6 @@ public class CalendarEventSpec {
|
||||
public int durationInSeconds;
|
||||
public String title;
|
||||
public String description;
|
||||
public String location;
|
||||
public boolean allDay;
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.CalendarContract.Instances;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CalendarEvents {
|
||||
|
||||
@ -47,7 +47,8 @@ public class CalendarEvents {
|
||||
Instances.TITLE,
|
||||
Instances.DESCRIPTION,
|
||||
Instances.EVENT_LOCATION,
|
||||
Instances.CALENDAR_DISPLAY_NAME
|
||||
Instances.CALENDAR_DISPLAY_NAME,
|
||||
Instances.ALL_DAY
|
||||
};
|
||||
|
||||
private static final int lookahead_days = 7;
|
||||
@ -62,16 +63,16 @@ public class CalendarEvents {
|
||||
private boolean fetchSystemEvents(Context mContext) {
|
||||
|
||||
Calendar cal = GregorianCalendar.getInstance();
|
||||
Long dtStart = cal.getTime().getTime();
|
||||
Long dtStart = cal.getTimeInMillis();
|
||||
cal.add(Calendar.DATE, lookahead_days);
|
||||
Long dtEnd = cal.getTime().getTime();
|
||||
Long dtEnd = cal.getTimeInMillis();
|
||||
|
||||
Uri.Builder eventsUriBuilder = CalendarContract.Instances.CONTENT_URI.buildUpon();
|
||||
Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon();
|
||||
ContentUris.appendId(eventsUriBuilder, dtStart);
|
||||
ContentUris.appendId(eventsUriBuilder, dtEnd);
|
||||
Uri eventsUri = eventsUriBuilder.build();
|
||||
|
||||
try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, CalendarContract.Instances.BEGIN + " ASC")) {
|
||||
try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, Instances.BEGIN + " ASC")) {
|
||||
if (evtCursor == null || evtCursor.getCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
@ -83,7 +84,8 @@ public class CalendarEvents {
|
||||
evtCursor.getString(4),
|
||||
evtCursor.getString(5),
|
||||
evtCursor.getString(6),
|
||||
evtCursor.getString(7)
|
||||
evtCursor.getString(7),
|
||||
!evtCursor.getString(8).equals("0")
|
||||
);
|
||||
calendarEventList.add(calEvent);
|
||||
}
|
||||
@ -99,8 +101,9 @@ public class CalendarEvents {
|
||||
private String description;
|
||||
private String location;
|
||||
private String calName;
|
||||
private boolean allDay;
|
||||
|
||||
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName) {
|
||||
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, boolean allDay) {
|
||||
this.begin = begin;
|
||||
this.end = end;
|
||||
this.id = id;
|
||||
@ -108,6 +111,7 @@ public class CalendarEvents {
|
||||
this.description = description;
|
||||
this.location = location;
|
||||
this.calName = calName;
|
||||
this.allDay = allDay;
|
||||
}
|
||||
|
||||
public long getBegin() {
|
||||
@ -155,5 +159,38 @@ public class CalendarEvents {
|
||||
return calName;
|
||||
}
|
||||
|
||||
public boolean isAllDay() {
|
||||
return allDay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof CalendarEvent) {
|
||||
CalendarEvent e = (CalendarEvent) other;
|
||||
return (this.getId() == e.getId()) &&
|
||||
Objects.equals(this.getTitle(), e.getTitle()) &&
|
||||
(this.getBegin() == e.getBegin()) &&
|
||||
Objects.equals(this.getLocation(), e.getLocation()) &&
|
||||
Objects.equals(this.getDescription(), e.getDescription()) &&
|
||||
(this.getEnd() == e.getEnd()) &&
|
||||
Objects.equals(this.getCalName(), e.getCalName()) &&
|
||||
(this.isAllDay() == e.isAllDay());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) id;
|
||||
result = 31 * result + Objects.hash(title);
|
||||
result = 31 * result + Long.valueOf(begin).hashCode();
|
||||
result = 31 * result + Objects.hash(location);
|
||||
result = 31 * result + Objects.hash(description);
|
||||
result = 31 * result + Long.valueOf(end).hashCode();
|
||||
result = 31 * result + Objects.hash(calName);
|
||||
result = 31 * result + Boolean.valueOf(allDay).hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public interface DeviceService extends EventHandler {
|
||||
String EXTRA_URI = "uri";
|
||||
String EXTRA_CONFIG = "config";
|
||||
String EXTRA_ALARMS = "alarms";
|
||||
String EXTRA_PERFORM_PAIR = "perform_pair";
|
||||
String EXTRA_CONNECT_FIRST_TIME = "connect_first_time";
|
||||
String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps";
|
||||
|
||||
String EXTRA_WEATHER_TIMESTAMP = "weather_timestamp";
|
||||
|
@ -62,6 +62,16 @@ public class MusicSpec {
|
||||
this.duration == musicSpec.duration &&
|
||||
this.trackCount == musicSpec.trackCount &&
|
||||
this.trackNr == musicSpec.trackNr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = artist != null ? artist.hashCode() : 0;
|
||||
result = 31 * result + (album != null ? album.hashCode() : 0);
|
||||
result = 31 * result + (track != null ? track.hashCode() : 0);
|
||||
result = 31 * result + duration;
|
||||
result = 31 * result + trackCount;
|
||||
result = 31 * result + trackNr;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ public enum NotificationType {
|
||||
GENERIC_ALARM_CLOCK(PebbleIconID.ALARM_CLOCK, PebbleColor.Red);
|
||||
// Note: if you add any more constants, update all clients as well
|
||||
|
||||
public int icon;
|
||||
public byte color;
|
||||
public final int icon;
|
||||
public final byte color;
|
||||
|
||||
NotificationType(int icon, byte color) {
|
||||
this.icon = icon;
|
||||
|
@ -88,6 +88,14 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation just calls #connect()
|
||||
*/
|
||||
@Override
|
||||
public boolean connectFirstTime() {
|
||||
return connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return gbDevice.isConnected();
|
||||
|
@ -41,9 +41,12 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothPairingRequestReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.PebbleReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.PhoneCallReceiver;
|
||||
@ -113,6 +116,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAL
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES_TYPE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONFIG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
|
||||
@ -134,7 +138,6 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TYPE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTCONDITION;
|
||||
@ -164,9 +167,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
private MusicPlaybackReceiver mMusicPlaybackReceiver = null;
|
||||
private TimeChangeReceiver mTimeChangeReceiver = null;
|
||||
private BluetoothConnectReceiver mBlueToothConnectReceiver = null;
|
||||
private BluetoothPairingRequestReceiver mBlueToothPairingRequestReceiver = null;
|
||||
private AlarmClockReceiver mAlarmClockReceiver = null;
|
||||
|
||||
private AlarmReceiver mAlarmReceiver = null;
|
||||
private CalendarReceiver mCalendarReceiver = null;
|
||||
private Random mRandom = new Random();
|
||||
|
||||
private final String[] mMusicActions = {
|
||||
@ -202,7 +207,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
if (mGBDevice.equals(device)) {
|
||||
mGBDevice = device;
|
||||
boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized());
|
||||
setReceiversEnableState(enableReceivers);
|
||||
setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), DeviceHelper.getInstance().getCoordinator(device));
|
||||
GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context);
|
||||
} else {
|
||||
LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + mGBDevice);
|
||||
@ -239,7 +244,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
}
|
||||
|
||||
String action = intent.getAction();
|
||||
boolean pair = intent.getBooleanExtra(EXTRA_PERFORM_PAIR, false);
|
||||
boolean firstTime = intent.getBooleanExtra(EXTRA_CONNECT_FIRST_TIME, false);
|
||||
|
||||
if (action == null) {
|
||||
LOG.info("no action");
|
||||
@ -299,8 +304,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
DeviceSupport deviceSupport = mFactory.createDeviceSupport(gbDevice);
|
||||
if (deviceSupport != null) {
|
||||
setDeviceSupport(deviceSupport);
|
||||
if (pair) {
|
||||
deviceSupport.pair();
|
||||
if (firstTime) {
|
||||
deviceSupport.connectFirstTime();
|
||||
} else {
|
||||
deviceSupport.setAutoReconnect(autoReconnect);
|
||||
deviceSupport.connect();
|
||||
@ -393,7 +398,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
case ACTION_DISCONNECT: {
|
||||
mDeviceSupport.dispose();
|
||||
if (mGBDevice != null && mGBDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
|
||||
setReceiversEnableState(false);
|
||||
setReceiversEnableState(false, false, null);
|
||||
mGBDevice.setState(GBDevice.State.NOT_CONNECTED);
|
||||
mGBDevice.sendDeviceUpdateIntent(this);
|
||||
}
|
||||
@ -571,9 +576,33 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
}
|
||||
|
||||
|
||||
private void setReceiversEnableState(boolean enable) {
|
||||
private void setReceiversEnableState(boolean enable, boolean initialized, DeviceCoordinator coordinator) {
|
||||
LOG.info("Setting broadcast receivers to: " + enable);
|
||||
|
||||
if (enable && initialized && coordinator != null && coordinator.supportsCalendarEvents()) {
|
||||
if (mCalendarReceiver == null) {
|
||||
IntentFilter calendarIntentFilter = new IntentFilter();
|
||||
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
|
||||
calendarIntentFilter.addDataScheme("content");
|
||||
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
|
||||
mCalendarReceiver = new CalendarReceiver(mGBDevice);
|
||||
registerReceiver(mCalendarReceiver, calendarIntentFilter);
|
||||
}
|
||||
if (mAlarmReceiver == null) {
|
||||
mAlarmReceiver = new AlarmReceiver();
|
||||
registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM"));
|
||||
}
|
||||
} else {
|
||||
if (mCalendarReceiver != null) {
|
||||
unregisterReceiver(mCalendarReceiver);
|
||||
mCalendarReceiver = null;
|
||||
}
|
||||
if (mAlarmReceiver != null) {
|
||||
unregisterReceiver(mAlarmReceiver);
|
||||
mAlarmReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
if (mPhoneCallReceiver == null) {
|
||||
mPhoneCallReceiver = new PhoneCallReceiver();
|
||||
@ -609,9 +638,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
mBlueToothConnectReceiver = new BluetoothConnectReceiver(this);
|
||||
registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
|
||||
}
|
||||
if (mAlarmReceiver == null) {
|
||||
mAlarmReceiver = new AlarmReceiver();
|
||||
registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM"));
|
||||
if (mBlueToothPairingRequestReceiver == null) {
|
||||
mBlueToothPairingRequestReceiver = new BluetoothPairingRequestReceiver(this);
|
||||
registerReceiver(mBlueToothPairingRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST));
|
||||
}
|
||||
if (mAlarmClockReceiver == null) {
|
||||
mAlarmClockReceiver = new AlarmClockReceiver();
|
||||
@ -645,9 +674,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
unregisterReceiver(mBlueToothConnectReceiver);
|
||||
mBlueToothConnectReceiver = null;
|
||||
}
|
||||
if (mAlarmReceiver != null) {
|
||||
unregisterReceiver(mAlarmReceiver);
|
||||
mAlarmReceiver = null;
|
||||
|
||||
if (mBlueToothPairingRequestReceiver != null) {
|
||||
unregisterReceiver(mBlueToothPairingRequestReceiver);
|
||||
mBlueToothPairingRequestReceiver = null;
|
||||
}
|
||||
if (mAlarmClockReceiver != null) {
|
||||
unregisterReceiver(mAlarmClockReceiver);
|
||||
@ -666,7 +696,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
super.onDestroy();
|
||||
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
||||
setReceiversEnableState(false); // disable BroadcastReceivers
|
||||
setReceiversEnableState(false, false, null); // disable BroadcastReceivers
|
||||
|
||||
setDeviceSupport(null);
|
||||
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
@ -52,6 +52,27 @@ public interface DeviceSupport extends EventHandler {
|
||||
*/
|
||||
boolean isConnected();
|
||||
|
||||
/**
|
||||
* Attempts an initial connection to the device, typically after the user "discovered"
|
||||
* and connects to it for the first time. Some implementations may perform an additional
|
||||
* initialization or application-level pairing compared to the regular {@link #connect()}.
|
||||
* <p/>
|
||||
* Implementations may perform the connection in a synchronous or asynchronous way.
|
||||
* Returns true if a connection attempt was made. If the implementation is synchronous
|
||||
* it may also return true if the connection was successfully established, however
|
||||
* callers shall not rely on that.
|
||||
* <p/>
|
||||
* The actual connection state change (successful or not) will be reported via the
|
||||
* #getDevice device as a device change Intent.
|
||||
*
|
||||
* Note: the default implementation {@link AbstractDeviceSupport#connectFirstTime()} just
|
||||
* calls {@link #connect()}
|
||||
*
|
||||
* @see #connect()
|
||||
* @see GBDevice#ACTION_DEVICE_CHANGED
|
||||
*/
|
||||
boolean connectFirstTime();
|
||||
|
||||
/**
|
||||
* Attempts to establish a connection to the device. Implementations may perform
|
||||
* the connection in a synchronous or asynchronous way.
|
||||
@ -62,6 +83,7 @@ public interface DeviceSupport extends EventHandler {
|
||||
* The actual connection state change (successful or not) will be reported via the
|
||||
* #getDevice device as a device change Intent.
|
||||
*
|
||||
* @see #connectFirstTime()
|
||||
* @see GBDevice#ACTION_DEVICE_CHANGED
|
||||
*/
|
||||
boolean connect();
|
||||
@ -92,14 +114,6 @@ public interface DeviceSupport extends EventHandler {
|
||||
*/
|
||||
boolean getAutoReconnect();
|
||||
|
||||
/**
|
||||
* Attempts to pair and connect this device with the gadget device. Success
|
||||
* will be reported via a device change Intent.
|
||||
*
|
||||
* @see GBDevice#ACTION_DEVICE_CHANGED
|
||||
*/
|
||||
void pair();
|
||||
|
||||
/**
|
||||
* Returns the associated device this instance communicates with.
|
||||
*/
|
||||
|
@ -0,0 +1,79 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener;
|
||||
|
||||
/**
|
||||
* Original source by xinghui - see https://gist.github.com/xinghui/b2ddd8cffe55c4b62f5d8846d5545bf9
|
||||
* NB: no license specified in the source code as of 2017-04-19
|
||||
*/
|
||||
public class NotificationCollectorMonitorService extends Service {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NotificationCollectorMonitorService.class);
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ensureCollectorRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void ensureCollectorRunning() {
|
||||
ComponentName collectorComponent = new ComponentName(this, NotificationListener.class);
|
||||
LOG.info("ensureCollectorRunning collectorComponent: " + collectorComponent);
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
boolean collectorRunning = false;
|
||||
List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);
|
||||
if (runningServices == null) {
|
||||
LOG.info("ensureCollectorRunning() runningServices is NULL");
|
||||
return;
|
||||
}
|
||||
for (ActivityManager.RunningServiceInfo service : runningServices) {
|
||||
if (service.service.equals(collectorComponent)) {
|
||||
LOG.warn("ensureCollectorRunning service - pid: " + service.pid + ", currentPID: " + Process.myPid() + ", clientPackage: " + service.clientPackage + ", clientCount: " + service.clientCount
|
||||
+ ", clientLabel: " + ((service.clientLabel == 0) ? "0" : "(" + getResources().getString(service.clientLabel) + ")"));
|
||||
if (service.pid == Process.myPid() /*&& service.clientCount > 0 && !TextUtils.isEmpty(service.clientPackage)*/) {
|
||||
collectorRunning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collectorRunning) {
|
||||
LOG.debug("ensureCollectorRunning: collector is running");
|
||||
return;
|
||||
}
|
||||
LOG.debug("ensureCollectorRunning: collector not running, reviving...");
|
||||
toggleNotificationListenerService();
|
||||
}
|
||||
|
||||
private void toggleNotificationListenerService() {
|
||||
LOG.debug("toggleNotificationListenerService() called");
|
||||
ComponentName thisComponent = new ComponentName(this, NotificationListener.class);
|
||||
PackageManager pm = getPackageManager();
|
||||
pm.setComponentEnabledSetting(thisComponent, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||
pm.setComponentEnabledSetting(thisComponent, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -72,6 +72,11 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||
return delegate.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectFirstTime() {
|
||||
return delegate.connectFirstTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
return delegate.connect();
|
||||
@ -112,11 +117,6 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||
return delegate.useAutoConnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
delegate.pair();
|
||||
}
|
||||
|
||||
private boolean checkBusy(String notificationKind) {
|
||||
if (!flags.contains(Flags.BUSY_CHECKING)) {
|
||||
return false;
|
||||
|
@ -101,6 +101,7 @@ public class BleNamesResolver {
|
||||
mServices.put("00001804-0000-1000-8000-00805f9b34fb", "Tx Power");
|
||||
mServices.put("0000fee0-0000-3512-2118-0009af100700", "(Propr: Xiaomi MiLi Service)");
|
||||
mServices.put("00001530-0000-3512-2118-0009af100700", "(Propr: Xiaomi Weight Service)");
|
||||
mServices.put("14701820-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Service)");
|
||||
|
||||
|
||||
mCharacteristics.put("00002a43-0000-1000-8000-00805f9b34fb", "Alert AlertCategory ID");
|
||||
@ -184,8 +185,10 @@ public class BleNamesResolver {
|
||||
mCharacteristics.put("00002a0e-0000-1000-8000-00805f9b34fb", "Time Zone");
|
||||
mCharacteristics.put("00002a07-0000-1000-8000-00805f9b34fb", "Tx Power Level");
|
||||
mCharacteristics.put("00002a45-0000-1000-8000-00805f9b34fb", "Unread Alert Status");
|
||||
|
||||
mValueFormats.put(Integer.valueOf(52), "32bit float");
|
||||
|
||||
mCharacteristics.put("14702856-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Control)");
|
||||
mCharacteristics.put("14702853-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Measurements)");
|
||||
mValueFormats.put(Integer.valueOf(52), "32bit float");
|
||||
mValueFormats.put(Integer.valueOf(50), "16bit float");
|
||||
mValueFormats.put(Integer.valueOf(34), "16bit signed int");
|
||||
mValueFormats.put(Integer.valueOf(36), "32bit signed int");
|
||||
|
@ -30,7 +30,7 @@ public class ValueDecoder {
|
||||
int percent = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
|
||||
if (percent > 100 || percent < 0) {
|
||||
LOG.warn("Unexpected percent value: " + percent + ": " + GattCharacteristic.toString(characteristic));
|
||||
percent = Math.max(100, Math.min(0, percent));
|
||||
percent = Math.min(100, Math.max(0, percent));
|
||||
}
|
||||
return percent;
|
||||
}
|
||||
|
@ -102,4 +102,8 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
|
||||
|
||||
secondsInactive += other.secondsInactive;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return steps != 0 || secondsInactive != 0 || heartRate != -1;
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60;
|
||||
private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30;
|
||||
|
||||
private int HELLO_PERIOD = 60;
|
||||
|
||||
private boolean mQuit = false;
|
||||
private HPlusSupport mHPlusSupport;
|
||||
|
||||
@ -76,6 +78,8 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
private Calendar mGetSleepTime = GregorianCalendar.getInstance();
|
||||
private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance();
|
||||
|
||||
private Calendar mHelloTime = GregorianCalendar.getInstance();
|
||||
|
||||
private boolean mSlotsInitialSync = true;
|
||||
|
||||
private HPlusDataRecordRealtime prevRealTimeRecord = null;
|
||||
@ -88,7 +92,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
|
||||
public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) {
|
||||
super(gbDevice, context);
|
||||
|
||||
LOG.info("Initializing HPlus Handler Thread");
|
||||
mQuit = false;
|
||||
|
||||
mHPlusSupport = hplusSupport;
|
||||
@ -137,6 +141,10 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
requestDaySummaryData();
|
||||
}
|
||||
|
||||
if (now.compareTo(mHelloTime) > 0) {
|
||||
sendHello();
|
||||
}
|
||||
|
||||
now = GregorianCalendar.getInstance();
|
||||
waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis())) - now.getTimeInMillis();
|
||||
}
|
||||
@ -152,10 +160,14 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
}
|
||||
|
||||
public void sync() {
|
||||
LOG.info("HPlus: Starting data synchronization");
|
||||
|
||||
mGetSleepTime.setTimeInMillis(0);
|
||||
mGetDaySlotsTime.setTimeInMillis(0);
|
||||
mGetDaySummaryTime.setTimeInMillis(0);
|
||||
mLastSleepDayReceived.setTimeInMillis(0);
|
||||
mHelloTime = GregorianCalendar.getInstance();
|
||||
mHelloTime.add(Calendar.SECOND, HELLO_PERIOD);
|
||||
|
||||
mSlotsInitialSync = true;
|
||||
mLastSlotReceived = -1;
|
||||
@ -163,19 +175,41 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
mCurrentDaySlot = null;
|
||||
mDaySlotRecords.clear();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("startSyncDayStats");
|
||||
try {
|
||||
if (!mHPlusSupport.isConnected())
|
||||
mHPlusSupport.connect();
|
||||
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID});
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION});
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA});
|
||||
TransactionBuilder builder = new TransactionBuilder("startSyncDayStats");
|
||||
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID});
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION});
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA});
|
||||
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
synchronized (waitObject) {
|
||||
waitObject.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendHello() {
|
||||
try {
|
||||
if (!mHPlusSupport.isConnected())
|
||||
mHPlusSupport.connect();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("hello");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO);
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
mHelloTime = GregorianCalendar.getInstance();
|
||||
mHelloTime.add(Calendar.SECOND, HELLO_PERIOD);
|
||||
|
||||
}
|
||||
/**
|
||||
* Process a message containing information regarding a day slot
|
||||
* A slot summarizes 10 minutes of data
|
||||
@ -190,7 +224,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
try{
|
||||
record = new HPlusDataRecordDaySlot(data);
|
||||
} catch(IllegalArgumentException e){
|
||||
LOG.debug((e.getMessage()));
|
||||
LOG.info((e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -254,6 +288,11 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
List<HPlusHealthActivitySample> samples = new ArrayList<>();
|
||||
|
||||
for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) {
|
||||
|
||||
//Invalid records (no data) will be ignored
|
||||
if (!storedRecord.isValid())
|
||||
continue;
|
||||
|
||||
HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp);
|
||||
|
||||
sample.setRawHPlusHealthData(storedRecord.getRawData());
|
||||
@ -269,9 +308,9 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
mDaySlotRecords.clear();
|
||||
|
||||
} catch (GBException ex) {
|
||||
LOG.debug((ex.getMessage()));
|
||||
LOG.info((ex.getMessage()));
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage());
|
||||
LOG.info(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +332,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
try{
|
||||
record = new HPlusDataRecordSleep(data);
|
||||
} catch(IllegalArgumentException e){
|
||||
LOG.debug((e.getMessage()));
|
||||
LOG.info((e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -326,7 +365,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
|
||||
provider.addGBActivitySample(sample);
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage());
|
||||
LOG.info(ex.getMessage());
|
||||
}
|
||||
|
||||
mGetSleepTime = GregorianCalendar.getInstance();
|
||||
@ -347,7 +386,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
try{
|
||||
record = new HPlusDataRecordRealtime(data);
|
||||
} catch(IllegalArgumentException e){
|
||||
LOG.debug((e.getMessage()));
|
||||
LOG.info((e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -397,9 +436,9 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
|
||||
//TODO: Handle Active Time. With Overlay?
|
||||
} catch (GBException ex) {
|
||||
LOG.debug((ex.getMessage()));
|
||||
LOG.info((ex.getMessage()));
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage());
|
||||
LOG.info(ex.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -417,7 +456,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
try{
|
||||
record = new HPlusDataRecordDaySummary(data);
|
||||
} catch(IllegalArgumentException e){
|
||||
LOG.debug((e.getMessage()));
|
||||
LOG.info((e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -437,9 +476,9 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
sample.setProvider(provider);
|
||||
provider.addGBActivitySample(sample);
|
||||
} catch (GBException ex) {
|
||||
LOG.debug((ex.getMessage()));
|
||||
LOG.info((ex.getMessage()));
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage());
|
||||
LOG.info(ex.getMessage());
|
||||
}
|
||||
|
||||
mGetDaySummaryTime = GregorianCalendar.getInstance();
|
||||
@ -468,10 +507,16 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
* Issue a message requesting the next batch of sleep data
|
||||
*/
|
||||
private void requestNextSleepData() {
|
||||
TransactionBuilder builder = new TransactionBuilder("requestSleepStats");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP});
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
try {
|
||||
if (!mHPlusSupport.isConnected())
|
||||
mHPlusSupport.connect();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("requestSleepStats");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP});
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
mGetSleepTime = GregorianCalendar.getInstance();
|
||||
mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_RETRY_PERIOD);
|
||||
@ -519,19 +564,31 @@ class HPlusHandlerThread extends GBDeviceIoThread {
|
||||
mLastSlotRequested = nextHour * 6 + (nextMinute / 10);
|
||||
|
||||
byte[] msg = new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY, hour, minute, nextHour, nextMinute};
|
||||
try {
|
||||
if (!mHPlusSupport.isConnected())
|
||||
mHPlusSupport.connect();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("getNextDaySlot");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, msg);
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
TransactionBuilder builder = new TransactionBuilder("getNextDaySlot");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, msg);
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Request a batch of data with the summary of the previous days
|
||||
*/
|
||||
public void requestDaySummaryData(){
|
||||
TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA});
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
try {
|
||||
if (!mHPlusSupport.isConnected())
|
||||
mHPlusSupport.connect();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary");
|
||||
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA});
|
||||
builder.queue(mHPlusSupport.getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
mGetDaySummaryTime = GregorianCalendar.getInstance();
|
||||
mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_RETRY_PERIOD);
|
||||
}
|
||||
|
@ -59,7 +59,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo;
|
||||
@ -94,8 +93,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
deviceType = type;
|
||||
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE);
|
||||
addSupportedService(HPlusConstants.UUID_SERVICE_HP);
|
||||
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
@ -106,7 +103,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.debug("Dispose");
|
||||
LOG.info("Dispose");
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(mReceiver);
|
||||
|
||||
@ -117,7 +114,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||
LOG.debug("Initializing");
|
||||
LOG.info("Initializing");
|
||||
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
|
||||
@ -417,12 +414,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
|
||||
LOG.debug("Pair");
|
||||
}
|
||||
|
||||
private void handleDeviceInfo(DeviceInfo info) {
|
||||
LOG.warn("Device info: " + info);
|
||||
}
|
||||
@ -430,7 +421,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
//TODO: Show different notifications according to source as Band supports this
|
||||
//LOG.debug("OnNotification: Title: "+notificationSpec.title+" Body: "+notificationSpec.body+" Source: "+notificationSpec.sourceName+" Sender: "+notificationSpec.sender+" Subject: "+notificationSpec.subject);
|
||||
//LOG.info("OnNotification: Title: "+notificationSpec.title+" Body: "+notificationSpec.body+" Source: "+notificationSpec.sourceName+" Sender: "+notificationSpec.sender+" Subject: "+notificationSpec.subject);
|
||||
showText(notificationSpec.title, notificationSpec.body);
|
||||
}
|
||||
|
||||
@ -441,40 +432,47 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
TransactionBuilder builder = new TransactionBuilder("time");
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("time");
|
||||
|
||||
setCurrentDate(builder);
|
||||
setCurrentTime(builder);
|
||||
setCurrentDate(builder);
|
||||
setCurrentTime(builder);
|
||||
|
||||
builder.queue(getQueue());
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("alarm");
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("alarm");
|
||||
for (Alarm alarm : alarms) {
|
||||
|
||||
for (Alarm alarm : alarms) {
|
||||
if (!alarm.isEnabled())
|
||||
continue;
|
||||
|
||||
if (!alarm.isEnabled())
|
||||
continue;
|
||||
if (alarm.isSmartWakeup()) //Not available
|
||||
continue;
|
||||
|
||||
if (alarm.isSmartWakeup()) //Not available
|
||||
continue;
|
||||
Calendar t = alarm.getAlarmCal();
|
||||
setAlarm(builder, t);
|
||||
builder.queue(getQueue());
|
||||
|
||||
Calendar t = alarm.getAlarmCal();
|
||||
setAlarm(builder, t);
|
||||
GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO);
|
||||
|
||||
return; //Only first alarm
|
||||
}
|
||||
|
||||
setAlarm(builder, null);
|
||||
builder.queue(getQueue());
|
||||
|
||||
GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO);
|
||||
|
||||
return; //Only first alarm
|
||||
GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
setAlarm(builder, null);
|
||||
builder.queue(getQueue());
|
||||
|
||||
GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO);
|
||||
|
||||
}
|
||||
|
||||
@ -491,7 +489,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||
LOG.debug("Canned Messages: " + cannedMessagesSpec);
|
||||
LOG.info("Canned Messages: " + cannedMessagesSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -547,39 +545,47 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onReboot() {
|
||||
getQueue().clear();
|
||||
try {
|
||||
getQueue().clear();
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("Shutdown");
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN});
|
||||
builder.queue(getQueue());
|
||||
TransactionBuilder builder = performInitialized("Shutdown");
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN});
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartRateTest() {
|
||||
getQueue().clear();
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("HeartRateTest");
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("HeartRateTest");
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ?
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ?
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
getQueue().clear();
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("realTimeHeartMeasurement");
|
||||
byte state;
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement");
|
||||
byte state;
|
||||
if (enable)
|
||||
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_ON;
|
||||
else
|
||||
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF;
|
||||
|
||||
if (enable)
|
||||
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_ON;
|
||||
else
|
||||
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF;
|
||||
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state});
|
||||
builder.queue(getQueue());
|
||||
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state});
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -638,13 +644,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
LOG.debug("Send Configuration: " + config);
|
||||
LOG.info("Send Configuration: " + config);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestNewFunction() {
|
||||
LOG.debug("Test New Function");
|
||||
LOG.info("Test New Function");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -712,7 +718,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
private void showText(String title, String body) {
|
||||
LOG.debug("Show Notification: " + title + " --> " + body);
|
||||
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("notification");
|
||||
|
||||
@ -800,7 +806,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
cs = new byte[]{HPlusConstants.transliterateMap.get(c)};
|
||||
} else {
|
||||
try {
|
||||
cs = c.toString().getBytes("GB2312");
|
||||
if (HPlusCoordinator.getLanguage(this.gbDevice.getAddress()) == HPlusConstants.ARG_LANGUAGE_CN)
|
||||
cs = c.toString().getBytes("GB2312");
|
||||
else
|
||||
cs = c.toString().getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
//Fallback. Result string may be strange, but better than nothing
|
||||
cs = c.toString().getBytes();
|
||||
@ -847,7 +856,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
return syncHelper.processIncomingDaySlotData(data);
|
||||
|
||||
default:
|
||||
LOG.debug("Unhandled characteristic changed: " + characteristicUUID);
|
||||
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + data[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -861,7 +870,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
String DEVINFO_STEP = getContext().getString(R.string.chart_steps) + ": ";
|
||||
String DEVINFO_DISTANCE = getContext().getString(R.string.distance) + ": ";
|
||||
String DEVINFO_CALORY = getContext().getString(R.string.calories) + ": ";
|
||||
String DEVINFO_HEART = "HR: ";
|
||||
String DEVINFO_HEART = getContext().getString(R.string.charts_legend_heartrate);
|
||||
|
||||
String info = "";
|
||||
if (record.steps > 0) {
|
||||
@ -881,7 +890,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
getDevice().addDeviceInfo(new GenericItem("", info));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.debug((e.getMessage()));
|
||||
LOG.info((e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,8 +197,6 @@ public class LiveviewIoThread extends GBDeviceIoThread {
|
||||
break;
|
||||
case HEADER_LEN:
|
||||
int headerSize = 0xff & incoming[0];
|
||||
if (headerSize < 0)
|
||||
throw new IOException();
|
||||
state = ReaderState.HEADER;
|
||||
incoming = new byte[headerSize];
|
||||
break;
|
||||
|
@ -217,12 +217,13 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
public boolean connectFirstTime() {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (connect()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public DeviceInfo getDeviceInfo() {
|
||||
|
@ -267,13 +267,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
public boolean connectFirstTime() {
|
||||
needsAuth = true;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (connect()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return super.connect();
|
||||
}
|
||||
|
||||
private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
||||
@ -810,7 +806,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
private void handleButtonPressed(byte[] value) {
|
||||
LOG.info("Button pressed: " + value);
|
||||
LOG.info("Button pressed");
|
||||
logMessageContent(value);
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@ -216,7 +216,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation {
|
||||
int len = value.length;
|
||||
|
||||
if (len % 4 != 1) {
|
||||
throw new AssertionError("Unexpected activity array size: " + value);
|
||||
throw new AssertionError("Unexpected activity array size: " + len);
|
||||
}
|
||||
|
||||
for (int i = 1; i < len; i+=4) {
|
||||
|
@ -69,8 +69,8 @@ class AppMessageHandlerZalewszczak extends AppMessageHandler {
|
||||
}
|
||||
|
||||
ArrayList<Pair<Integer, Object>> pairs = new ArrayList<>(2);
|
||||
pairs.add(new Pair<>(KEY_TEMP, (Object) (Math.round(weatherSpec.currentTemp - 273) + "C")));
|
||||
pairs.add(new Pair<>(KEY_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode))));
|
||||
pairs.add(new Pair<Integer, Object>(KEY_TEMP, weatherSpec.currentTemp - 273 + "C"));
|
||||
pairs.add(new Pair<Integer, Object>(KEY_ICON, getIconForConditionCode(weatherSpec.currentConditionCode)));
|
||||
byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs);
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length);
|
||||
|
@ -31,15 +31,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
class DatalogSessionAnalytics extends DatalogSession {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionAnalytics.class);
|
||||
private GBDeviceEventBatteryInfo mGBDeviceEventBatteryInfo = new GBDeviceEventBatteryInfo();
|
||||
private GBDevice mGBDevice;
|
||||
|
||||
DatalogSessionAnalytics(byte id, UUID uuid, int timestamp, int tag, byte itemType, short itemSize, GBDevice device) {
|
||||
super(id, uuid, timestamp, tag, itemType, itemSize);
|
||||
if (mGBDevice == null || !device.equals(mGBDevice)) { //prevent showing information of other pebble watches when switching devices
|
||||
mGBDevice = device;
|
||||
mGBDeviceEventBatteryInfo.state = BatteryState.UNKNOWN;
|
||||
}
|
||||
|
||||
// The default notification should not be too bad (one per hour) but we can override this if needed
|
||||
//mGBDevice.setBatteryThresholdPercent((short) 5);
|
||||
|
||||
@ -56,11 +50,13 @@ class DatalogSessionAnalytics extends DatalogSession {
|
||||
datalogMessage.position(datalogMessage.position() + 12);
|
||||
short reportedMilliVolts = datalogMessage.getShort();
|
||||
|
||||
LOG.info("Battery reading for TS " + messageTS + " is: " + reportedMilliVolts + " milliVolts, mapped to percentage: " + milliVoltstoPercentage(reportedMilliVolts));
|
||||
datalogMessage.position(datalogMessage.position() + 2);
|
||||
byte reportedPercentage = datalogMessage.get();
|
||||
|
||||
LOG.info("Battery reading for TS " + messageTS + " is: " + reportedMilliVolts + " milliVolts, percentage: " + reportedPercentage);
|
||||
if (messageTS > 0 && reportedMilliVolts < 5000) { //some safety checks
|
||||
// mGBDeviceEventBatteryInfo.state = BatteryState.BATTERY_NORMAL; //uncoomment this to show the battery status in the control center
|
||||
mGBDeviceEventBatteryInfo.level = milliVoltstoPercentage(reportedMilliVolts);
|
||||
mGBDeviceEventBatteryInfo.state = BatteryState.BATTERY_NORMAL;
|
||||
mGBDeviceEventBatteryInfo.level = reportedPercentage;
|
||||
|
||||
return new GBDeviceEvent[]{mGBDeviceEventBatteryInfo, null};
|
||||
} else { //invalid data, but we ack nevertheless
|
||||
@ -68,30 +64,4 @@ class DatalogSessionAnalytics extends DatalogSession {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private short milliVoltstoPercentage(short batteryMilliVolts) {
|
||||
if (batteryMilliVolts > 4145) { //(4146 is still 100, next reported value is already 90)
|
||||
return 100;
|
||||
} else if (batteryMilliVolts > 4053) { //(4054 is still 90, next reported value is already 80)
|
||||
return 90;
|
||||
} else if (batteryMilliVolts > 4000) { //guessed
|
||||
return 80;
|
||||
} else if (batteryMilliVolts > 3890) { //3890 was already 60
|
||||
return 70;
|
||||
} else if (batteryMilliVolts > 3855) { //probably
|
||||
return 60;
|
||||
} else if (batteryMilliVolts > 3780) { //3781 is still 50, next reading is 3776 but percentage on pebble unknown
|
||||
return 50;
|
||||
} else if (batteryMilliVolts >= 3750) { //3750 is still 40, next reported value is 3746 and already 30
|
||||
return 40;
|
||||
} else if (batteryMilliVolts > 3720) { //3723 is still 30, next reported value is 3719 and already 20
|
||||
return 30;
|
||||
} else if (batteryMilliVolts > 3680) { //3683 is still 20, next reported value is 3675 and already 10
|
||||
return 20;
|
||||
} else if (batteryMilliVolts > 3650) { //3657 is still 10
|
||||
return 10;
|
||||
} else {
|
||||
return 0; //or -1 for invalid?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -525,7 +525,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
iconId = PebbleIconID.TIMELINE_CALENDAR;
|
||||
}
|
||||
|
||||
return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) calendarEventSpec.durationInSeconds, iconId, calendarEventSpec.title, calendarEventSpec.description);
|
||||
return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) (calendarEventSpec.durationInSeconds / 60), iconId, calendarEventSpec.title, calendarEventSpec.description);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -841,6 +841,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
private byte[] encodeTimelinePin(UUID uuid, int timestamp, short duration, int icon_id, String title, String subtitle) {
|
||||
final short TIMELINE_PIN_LENGTH = 46;
|
||||
|
||||
//FIXME: dont depend layout on icon :P
|
||||
byte layout_id = 0x01;
|
||||
if (icon_id == PebbleIconID.TIMELINE_CALENDAR) {
|
||||
layout_id = 0x02;
|
||||
}
|
||||
icon_id |= 0x80000000;
|
||||
byte attributes_count = 2;
|
||||
byte actions_count = 0;
|
||||
@ -865,8 +870,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
buf.putShort(duration);
|
||||
buf.put((byte) 0x02); // type (0x02 = pin)
|
||||
buf.putShort((short) 0x0001); // flags 0x0001 = ?
|
||||
buf.put((byte) 0x01); // layout was (0x02 = pin?), 0x01 needed for subtitle but seems to do no harm if there isn't one
|
||||
|
||||
buf.put(layout_id); // layout was (0x02 = pin?), 0x01 needed for subtitle but seems to do no harm if there isn't one
|
||||
buf.putShort((short) attributes_length); // total length of all attributes and actions in bytes
|
||||
buf.put(attributes_count);
|
||||
buf.put(actions_count);
|
||||
|
@ -124,11 +124,6 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
|
||||
}
|
||||
|
||||
private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) {
|
||||
LOG.warn("Device info: " + info);
|
||||
versionCmd.hwVersion = info.getHardwareRevision();
|
||||
|
@ -68,12 +68,6 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
// Default implementation does no manual pairing, use the Android
|
||||
// pairing dialog instead.
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily creates and returns the GBDeviceProtocol instance to be used.
|
||||
*/
|
||||
|
@ -17,9 +17,7 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@ -71,16 +69,17 @@ public class CheckSums {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readAll(InputStream in, long maxLen) throws IOException {
|
||||
// copy&paste of FileUtils.readAll() to have it free from Android dependencies
|
||||
private static byte[] readAll(InputStream in, long maxLen) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(8192, in.available()));
|
||||
byte[] buf = new byte[8192];
|
||||
int read = 0;
|
||||
int read;
|
||||
long totalRead = 0;
|
||||
while ((read = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, read);
|
||||
totalRead += read;
|
||||
if (totalRead > maxLen) {
|
||||
throw new IOException("Too much data to read into memory. Got already " + totalRead + buf);
|
||||
throw new IOException("Too much data to read into memory. Got already " + totalRead);
|
||||
}
|
||||
}
|
||||
return out.toByteArray();
|
||||
|
@ -54,7 +54,7 @@ public class DateTimeUtils {
|
||||
DurationFormatter df = DurationFormatter.Builder.SYMBOLS
|
||||
.maximum(TimeUnit.DAYS)
|
||||
.minimum(TimeUnit.MINUTES)
|
||||
.suppressZeros(DurationFormatter.SuppressZeros.LEADING)
|
||||
.suppressZeros(DurationFormatter.SuppressZeros.LEADING, DurationFormatter.SuppressZeros.TRAILING)
|
||||
.maximumAmountOfUnitsToShow(2)
|
||||
.build();
|
||||
return df.format(duration, unit);
|
||||
|
@ -54,7 +54,9 @@ public class FileUtils {
|
||||
if (!sourceFile.exists()) {
|
||||
throw new IOException("Does not exist: " + sourceFile.getAbsolutePath());
|
||||
}
|
||||
copyFile(new FileInputStream(sourceFile), new FileOutputStream(destFile));
|
||||
try (FileInputStream in = new FileInputStream(sourceFile); FileOutputStream out = new FileOutputStream(destFile)) {
|
||||
copyFile(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyFile(FileInputStream sourceStream, FileOutputStream destStream) throws IOException {
|
||||
@ -229,13 +231,13 @@ public class FileUtils {
|
||||
public static byte[] readAll(InputStream in, long maxLen) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(8192, in.available()));
|
||||
byte[] buf = new byte[8192];
|
||||
int read = 0;
|
||||
int read;
|
||||
long totalRead = 0;
|
||||
while ((read = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, read);
|
||||
totalRead += read;
|
||||
if (totalRead > maxLen) {
|
||||
throw new IOException("Too much data to read into memory. Got already " + totalRead + buf);
|
||||
throw new IOException("Too much data to read into memory. Got already " + totalRead);
|
||||
}
|
||||
}
|
||||
return out.toByteArray();
|
||||
|
@ -61,4 +61,9 @@ public class Version implements Comparable<Version> {
|
||||
return false;
|
||||
return this.compareTo((Version) that) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return version.hashCode();
|
||||
}
|
||||
}
|
5
app/src/main/res/color/alarm_dow.xml
Normal file
5
app/src/main/res/color/alarm_dow.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/colorAccent" android:state_checked="true" />
|
||||
<item android:color="?attr/textColorPrimary" />
|
||||
</selector>
|
@ -9,32 +9,21 @@
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.AlarmDetails">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_smart_wakeup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_smart_wakeup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_smart_wakeup"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/alarm_cb_smart_wakeup"
|
||||
android:text="@string/alarm_smart_wakeup" />
|
||||
</LinearLayout>
|
||||
android:layout_marginStart="4dp"
|
||||
android:drawableStart="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_smart_wakeup"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<TimePicker
|
||||
android:id="@+id/alarm_time_picker"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:timePickerMode="clock" />
|
||||
android:layout_marginBottom="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dowSelector"
|
||||
@ -43,174 +32,83 @@
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_monday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_mon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_mon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_mon"
|
||||
android:text="@string/alarm_mon_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_mon_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_tue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_tue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_tue"
|
||||
android:text="@string/alarm_tue_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_tuesday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_tue_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_wed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_wed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_wed"
|
||||
android:text="@string/alarm_wed_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_wednesday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_wed_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_thu"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_thu"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_thu"
|
||||
android:text="@string/alarm_thu_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_thursday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_thu_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_fri"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_fri"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_fri"
|
||||
android:text="@string/alarm_fri_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_friday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_fri_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_sat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_sat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_sat"
|
||||
android:text="@string/alarm_sat_short" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_saturday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/alarm_cb_sun"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/alarm_label_cb_sun"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:labelFor="@id/alarm_cb_sun"
|
||||
android:text="@string/alarm_sun_short" />
|
||||
|
||||
</LinearLayout>
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_sat_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<android.support.v7.widget.AppCompatCheckedTextView
|
||||
android:id="@+id/alarm_cb_sunday"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_sun_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -2,17 +2,24 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.AppBlacklistActivity">
|
||||
|
||||
<ListView
|
||||
android:layout_width="wrap_content"
|
||||
<android.support.v7.widget.SearchView
|
||||
android:id="@+id/appListViewSearch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/appListView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp">
|
||||
|
||||
</android.support.v7.widget.SearchView>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/appListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/appListViewSearch"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:divider="@null" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -25,7 +25,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:text="00:00"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
@ -43,7 +46,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true" />
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_margin="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dowSelector"
|
||||
@ -59,8 +63,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_mon_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -71,8 +74,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_tue_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -83,8 +85,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_wed_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -95,8 +96,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_thu_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -107,8 +107,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_fri_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -119,8 +118,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_sat_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
@ -131,8 +129,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableTop="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:textColor="@color/alarm_dow"
|
||||
android:gravity="center"
|
||||
android:text="@string/alarm_sun_short"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
@ -3,7 +3,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/activatedBackgroundIndicator"
|
||||
android:padding="8dp">
|
||||
android:minHeight="60dp">
|
||||
|
||||
|
||||
<CheckBox
|
||||
@ -12,6 +12,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false" />
|
||||
|
||||
@ -19,7 +21,12 @@
|
||||
android:id="@+id/item_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_toRightOf="@+id/item_checkbox" />
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_toEndOf="@+id/item_checkbox"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
@ -27,24 +34,23 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@+id/item_image"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_name"
|
||||
style="@style/Base.TextAppearance.AppCompat.SearchResult.Title"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollHorizontally="false"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
android:text="Item Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_details"
|
||||
style="@style/Base.TextAppearance.AppCompat.SearchResult"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Item Description" />
|
||||
android:text="Item Description"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -1,6 +1,9 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Gadgetbridge</string>
|
||||
<string name="title_activity_controlcenter">Gadgetbridge</string>
|
||||
<string name="action_settings">Nastavení</string>
|
||||
<string name="action_debug">Ladění</string>
|
||||
<string name="action_quit">Ukončit</string>
|
||||
<string name="controlcenter_fetch_activity_data">Načíst data</string>
|
||||
<string name="controlcenter_start_sleepmonitor">Monitor spánku (alfa)</string>
|
||||
@ -10,6 +13,13 @@
|
||||
<string name="controlcenter_delete_device">Odstranit zařízení</string>
|
||||
<string name="controlcenter_delete_device_name">Odstranit %1$s</string>
|
||||
<string name="controlcenter_delete_device_dialogmessage">Odstraní zařízení a vymaže všechna data!</string>
|
||||
<string name="controlcenter_navigation_drawer_open">Otevřít navigační lištu</string>
|
||||
<string name="controlcenter_navigation_drawer_close">Zavřít navigační lištu</string>
|
||||
<string name="controlcenter_snackbar_need_longpress">Podržte kartu déle pro odpojení</string>
|
||||
<string name="controlcenter_snackbar_disconnecting">Odpojuji</string>
|
||||
<string name="controlcenter_snackbar_connecting">Připojuji</string>
|
||||
<string name="controlcenter_snackbar_requested_screenshot">Sejmout snímek obrazovky</string>
|
||||
<string name="title_activity_debug">Ladění</string>
|
||||
<!--Strings related to AppManager-->
|
||||
<string name="title_activity_appmanager">Manažer aplikací</string>
|
||||
<string name="appmanager_cached_watchapps_watchfaces">Aplikace v cache</string>
|
||||
@ -53,9 +63,12 @@
|
||||
<string name="pref_theme_dark">Tmavé</string>
|
||||
<string name="pref_title_language">Jazyk</string>
|
||||
<string name="pref_title_minimize_priority">Skrýt notifikace Gadgetbridge</string>
|
||||
<string name="pref_summary_minimize_priority_off">Ikona ve stavové liště a notifikace na zamčeném displeji budou zobrazeny</string>
|
||||
<string name="pref_summary_minimize_priority_on">Ikona ve stavové liště a notifikace na zamčeném displeji nebudou zobrazeny</string>
|
||||
<string name="pref_header_notifications">Notifikace</string>
|
||||
<string name="pref_title_notifications_repetitions">Opakování</string>
|
||||
<string name="pref_title_notifications_call">Volání</string>
|
||||
<string name="pref_title_notifications_sms">SMS</string>
|
||||
<string name="pref_title_notifications_pebblemsg">Zpráva Pebble</string>
|
||||
<string name="pref_summary_notifications_pebblemsg">Podpora pro aplikace, které posílají notifikace do Pebble via PebbleKit.</string>
|
||||
<string name="pref_title_notifications_generic">Obecná podpora notifikací</string>
|
||||
@ -112,6 +125,7 @@
|
||||
<string name="pref_summary_pebble_forceuntested">Povolí funkce, které nebyl testovány. POVOLTE, JEN POKUD VÍTE, CO DĚLÁTE!</string>
|
||||
<string name="pref_title_pebble_forcele">Vždy preferovat BLE</string>
|
||||
<string name="pref_summary_pebble_forcele">Použít experimentální podporu Pebble LE pro všechny Pebble místo BT classic, vyžaduje spárování \"Pebble LE\" po připojení bez LE</string>
|
||||
<string name="pref_title_pebble_mtu_limit">Pebble 2/LE GATT MTU limit</string>
|
||||
<string name="pref_summary_pebble_mtu_limit">Pokud vaše Pebble 2/Pebble LE nepracuje jak má, zkuste toto nastavení pro omezení MTU (povolený rozsah 20-512)</string>
|
||||
<string name="pref_title_pebble_enable_applogs">Zapnout logování Watch App</string>
|
||||
<string name="pref_summary_pebble_enable_applogs">Logy od watch app budou logovány v Gadgetbridge (vyžaduje znovupřipojení)</string>
|
||||
@ -127,7 +141,10 @@
|
||||
<string name="connecting">připojování</string>
|
||||
<string name="connected">připojeno</string>
|
||||
<string name="unknown_state">neznámý stav</string>
|
||||
<string name="connectionstate_hw_fw">HW: %1$s FW: %2$s</string>
|
||||
<string name="connectionstate_fw">FW: %1$s</string>
|
||||
<string name="_unknown_">(neznámé)</string>
|
||||
<string name="test">Test</string>
|
||||
<string name="test_notification">Test notifikací</string>
|
||||
<string name="this_is_a_test_notification_from_gadgetbridge">Toto je notifikace z Gadgetbridge</string>
|
||||
<string name="bluetooth_is_not_supported_">BT není podporován.</string>
|
||||
@ -150,6 +167,7 @@
|
||||
<string name="discovery_stop_scanning">Zastavit hledání</string>
|
||||
<string name="discovery_start_scanning">Spustit hledání</string>
|
||||
<string name="action_discover">Připojit nové zařízení</string>
|
||||
<string name="device_with_rssi">%1$s (%2$s)</string>
|
||||
<string name="title_activity_android_pairing">Párovat zařízení</string>
|
||||
<string name="android_pairing_hint">Použijte párování BT Androidu pro spárování se zařízením. </string>
|
||||
<string name="title_activity_mi_band_pairing">Párovat Mi Band</string>
|
||||
@ -169,6 +187,7 @@
|
||||
<string name="miband_pairing_using_dummy_userdata">Data uživatele nejsou platná, nyní používám vzorová.</string>
|
||||
<string name="miband_pairing_tap_hint">Když Mi Band zavibruje a blikne, dotkněte se jej několikrát po sobě.</string>
|
||||
<string name="appinstaller_install">Instalovat</string>
|
||||
<string name="discovery_connected_devices_hint">Nastavte své zařízení pro vyhledání. Již připojená zařízení nebudou vyhledána. Zapněte lokalizaci (GPS) pro mobily s Androidem 6 a vyšším. Vypněte hlídání soukromí pro Gadgetbridge, protože může způsobit nestabilitu mobilu. Pokud není zařízení vyhledáno během několika minut, zkuste to znovu po restartu mobilu.</string>
|
||||
<string name="discovery_note">Poznámka:</string>
|
||||
<string name="candidate_item_device_image">Obraz zařízení</string>
|
||||
<string name="miband_prefs_alias">Jméno/přezdívka</string>
|
||||
@ -180,6 +199,7 @@
|
||||
<string name="sleep_activity_date_range">Z %1$s do %2$s</string>
|
||||
<string name="miband_prefs_wearside">Nosíte vlevo nebo vpravo?</string>
|
||||
<string name="pref_screen_vibration_profile">Profil vibrací</string>
|
||||
<string name="vibration_profile_staccato">Staccato</string>
|
||||
<string name="vibration_profile_short">Krátké</string>
|
||||
<string name="vibration_profile_medium">Střední</string>
|
||||
<string name="vibration_profile_long">Dlouhé</string>
|
||||
@ -228,15 +248,19 @@
|
||||
<string name="notif_battery_low_bigtext_number_of_charges">Počet nabití: %s</string>
|
||||
<string name="sleepchart_your_sleep">Váš spánek</string>
|
||||
<string name="weeksleepchart_sleep_a_week">Spánek za týden</string>
|
||||
<string name="weeksleepchart_today_sleep_description">Spánek dnes, cíl: %1$s</string>
|
||||
<string name="weekstepschart_steps_a_week">Kroky za týden</string>
|
||||
<string name="activity_sleepchart_activity_and_sleep">Vaše aktivita a spánek</string>
|
||||
<string name="updating_firmware">Nahrávám firmware...</string>
|
||||
<string name="fwapp_install_device_not_ready">Soubor nelze nainstalovat, zařízení není připraveno.</string>
|
||||
<string name="miband_installhandler_miband_firmware">Mi Band Firmware %1$s</string>
|
||||
<string name="miband_fwinstaller_compatible_version">Kompatibilní verze</string>
|
||||
<string name="miband_fwinstaller_untested_version">Netestovaná verze!</string>
|
||||
<string name="fwappinstaller_connection_state">Připojení k zařízení: %1$s</string>
|
||||
<string name="pbw_installhandler_pebble_firmware">Pebble Firmware %1$s</string>
|
||||
<string name="pbwinstallhandler_correct_hw_revision">Správná revize HW</string>
|
||||
<string name="pbwinstallhandler_incorrect_hw_revision">Revize HW není správná!</string>
|
||||
<string name="pbwinstallhandler_app_item">%1$s (%2$s)</string>
|
||||
<string name="updatefirmwareoperation_updateproblem_do_not_reboot">Při přenosu firmwaru nastaly potíže. Nerestartujte svůj Mi Band!</string>
|
||||
<string name="updatefirmwareoperation_metadata_updateproblem">Problém při přenosu matadat firmware</string>
|
||||
<string name="updatefirmwareoperation_update_complete">Instalace firmware je kompletní</string>
|
||||
@ -269,6 +293,7 @@
|
||||
<string name="miband_prefs_device_time_offset_hours">Časový posun zařízení v hodinách (pro zjišťování spánku směnařů)</string>
|
||||
<string name="miband2_prefs_dateformat">Mi2: formát data</string>
|
||||
<string name="dateformat_time">Čas</string>
|
||||
<string name="dateformat_date_time"><![CDATA[Time & Date]]></string>
|
||||
<string name="mi2_prefs_activate_display_on_lift">Zapnout displej při zvednutí</string>
|
||||
<string name="FetchActivityOperation_about_to_transfer_since">Přenáším data od %1$s</string>
|
||||
<string name="waiting_for_reconnect">čekání na znovupřipojení</string>
|
||||
@ -279,10 +304,14 @@
|
||||
<string name="activity_prefs_weight_kg">Váha v kg</string>
|
||||
<string name="authenticating">ověřování</string>
|
||||
<string name="authentication_required">ověřování vyžadováno</string>
|
||||
<string name="appwidget_text">Spí...</string>
|
||||
<string name="add_widget">Přidat widget</string>
|
||||
<string name="activity_prefs_sleep_duration">Preferovaná doba spánku v hodinách</string>
|
||||
<string name="appwidget_alarms_set">Budík nastaven na %1$02d:%2$02d</string>
|
||||
<string name="device_hw">HW: %1$s</string>
|
||||
<string name="device_fw">FW: %1$s</string>
|
||||
<string name="error_creating_directory_for_logfiles">Chyba při vytváření adresáře pro logy: %1$s</string>
|
||||
<string name="DEVINFO_HR_VER">Tep:</string>
|
||||
<string name="updatefirmwareoperation_update_in_progress">Probíhá aktualizace firmware</string>
|
||||
<string name="updatefirmwareoperation_firmware_not_sent">Firmware neodeslán</string>
|
||||
<string name="charts_legend_heartrate">Srdeční tep</string>
|
||||
@ -321,5 +350,10 @@
|
||||
<string name="pref_summary_setup_bt_pairing">Toto vypněte v případě problémů s připojením</string>
|
||||
<string name="unit_metric">Metrické</string>
|
||||
<string name="unit_imperial">Imperiální</string>
|
||||
<string name="timeformat_24h">24h</string>
|
||||
<string name="timeformat_am_pm">dop./odp.</string>
|
||||
<string name="pref_screen_notification_profile_alarm_clock">Budík</string>
|
||||
<string name="StringUtils_sender">(%1$s)</string>
|
||||
<string name="find_device_you_found_it">Nalezeno!</string>
|
||||
<string name="miband2_prefs_timeformat">Formát času Mi2</string>
|
||||
</resources>
|
||||
|
@ -84,6 +84,7 @@
|
||||
<string name="pref_title_call_privacy_mode">Privatsphäre-Modus für Anrufe</string>
|
||||
<string name="pref_call_privacy_mode_off">Zeige Name und Telefonnumer</string>
|
||||
<string name="pref_call_privacy_mode_name">Verstecke den Namen aber zeige die Telefonnummer an</string>
|
||||
<string name="pref_call_privacy_mode_number">Zeige den Namen an aber verstecke die Telefonnummer</string>
|
||||
<string name="pref_call_privacy_mode_complete">Verstecke Name und Telefonnummer</string>
|
||||
<string name="pref_blacklist">App-Benachrichtigungen blockieren</string>
|
||||
<string name="pref_header_cannned_messages">Vorgefertigte Nachrichten</string>
|
||||
@ -356,4 +357,7 @@
|
||||
<string name="StringUtils_sender"> (%1$s)</string>
|
||||
<string name="find_device_you_found_it">Gefunden!</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: Uhrzeit-Format</string>
|
||||
<string name="mi2_fw_installhandler_fw53_hint">Installiere Version %1$s vor dem Installieren der Firmware!</string>
|
||||
<string name="mi2_enable_text_notifications">Text Benachrichtigung</string>
|
||||
<string name="off">aus</string>
|
||||
</resources>
|
||||
|
@ -268,6 +268,8 @@
|
||||
<string name="updatefirmwareoperation_update_complete_rebooting">Instalación del firmware completa, reiniciando dispositivo...</string>
|
||||
<string name="updatefirmwareoperation_write_failed">Falló la escritura del firmware</string>
|
||||
<string name="chart_steps">Pasos</string>
|
||||
<string name="calories">Calorías</string>
|
||||
<string name="distance">Distancia</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>
|
||||
@ -325,11 +327,13 @@
|
||||
<string name="activity_db_management_merge_old_title">Borrar la base de datos antigua</string>
|
||||
<string name="dbmanagementactivvity_cannot_access_export_path">No se puede acceder a la ruta para exportar . Por favor, contacta con los desarrolladores.</string>
|
||||
<string name="dbmanagementactivity_exported_to">Exportado a: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">Error exportando DB: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">Error de exportación de la base de datos: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_shared">Error de exportación de las preferencias: %1$s</string>
|
||||
<string name="dbmanagementactivity_import_data_title">¿Importar Datos?</string>
|
||||
<string name="dbmanagementactivity_overwrite_database_confirmation">¿Quiere sobreescribir la base de datos actual? Todos sus datos actuales (si los hay) se borrarán.</string>
|
||||
<string name="dbmanagementactivity_import_successful">Importado con éxito.</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">Error importando DB: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">Error de importación de la base de datos: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_importing_shared">Error de importación de las preferencias: %1$s</string>
|
||||
<string name="dbmanagementactivity_delete_activity_data_title">¿Quieres borrar los datos de actividad?</string>
|
||||
<string name="dbmanagementactivity_really_delete_entire_db">¿Quieres borrar la base de datos? Todos tus datos de actividad y la información sobre tus dispositivos se borrarán.</string>
|
||||
<string name="dbmanagementactivity_database_successfully_deleted">Datos borrados.</string>
|
||||
@ -361,4 +365,13 @@
|
||||
<string name="mi2_enable_text_notifications">Notificaciones textuales</string>
|
||||
<string name="mi2_enable_text_notifications_summary"><![CDATA[Needs firmware >= 1.0.1.28 and Mili_pro.ft* installed.]]></string>
|
||||
<string name="off">off</string>
|
||||
<string name="discovery_attempting_to_pair">Intento de emparejamiento con %1$s</string>
|
||||
<string name="discovery_bonding_failed_immediately">El enlace con %1$s falló instantáneamente</string>
|
||||
<string name="discovery_trying_to_connect_to">Intentando conectar con: %1$s</string>
|
||||
<string name="discovery_enable_bluetooth">Activa el Bluetooth para encontrar dispositivos.</string>
|
||||
<string name="discovery_successfully_bonded">Correctamente conectado con %1$s</string>
|
||||
<string name="discovery_pair_title">Emparejar con %1$s?</string>
|
||||
<string name="discovery_pair_question">Selecciona Emparejar para emparejar tus dispositivos. Si esto falla, prueba de nuevo sin emparejar.</string>
|
||||
<string name="discovery_yes_pair">Emparejar</string>
|
||||
<string name="discovery_dont_pair">No emparejar</string>
|
||||
</resources>
|
||||
|
@ -268,6 +268,8 @@
|
||||
<string name="updatefirmwareoperation_update_complete_rebooting">Installation complète du micrologiciel, redémarrage de l\'appareil</string>
|
||||
<string name="updatefirmwareoperation_write_failed">Échec lors de l\'écriture du micrologiciel</string>
|
||||
<string name="chart_steps">Pas</string>
|
||||
<string name="calories">Calories</string>
|
||||
<string name="distance">Distance</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>
|
||||
@ -327,11 +329,13 @@ NOTE: la base de données sera bien évidement plus grande !</string>
|
||||
<string name="activity_db_management_merge_old_title">Effacer l\'ancienne base de données</string>
|
||||
<string name="dbmanagementactivvity_cannot_access_export_path">Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs.</string>
|
||||
<string name="dbmanagementactivity_exported_to">Exporter vers : %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">Erreur d\'exportation BD: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">Erreur d\'exportation de la base de données: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_shared">Erreur d\'exportation des préférences: %1$s</string>
|
||||
<string name="dbmanagementactivity_import_data_title">Importer des données ?</string>
|
||||
<string name="dbmanagementactivity_overwrite_database_confirmation">Voulez-vous vraiment effacer la base de données actuelle ? Toutes vos données (si vous en avez) seront perdues.</string>
|
||||
<string name="dbmanagementactivity_import_successful">Importation réussie.</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">Erreur lors de l\'importation BD: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">Erreur lors de l\'importation de la base de données: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_importing_shared">Erreur d\'importation des préférences: %1$s</string>
|
||||
<string name="dbmanagementactivity_delete_activity_data_title">Détruire les anciennes données ?</string>
|
||||
<string name="dbmanagementactivity_really_delete_entire_db">Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données d\'activité et vos informations issues de vos appareils seront perdues.</string>
|
||||
<string name="dbmanagementactivity_database_successfully_deleted">Les données ont été effacées.</string>
|
||||
@ -363,4 +367,13 @@ NOTE: la base de données sera bien évidement plus grande !</string>
|
||||
<string name="mi2_enable_text_notifications">Notifications textuelles</string>
|
||||
<string name="mi2_enable_text_notifications_summary"><![CDATA[Needs firmware >= 1.0.1.28 and Mili_pro.ft* installed.]]></string>
|
||||
<string name="off">off</string>
|
||||
<string name="discovery_attempting_to_pair">Tentative d\'appairage avec %1$s</string>
|
||||
<string name="discovery_bonding_failed_immediately">Le lien avec %1$s a échoué instantanément</string>
|
||||
<string name="discovery_trying_to_connect_to">Tentative de connexion à: %1$s</string>
|
||||
<string name="discovery_enable_bluetooth">Activez le Bluetooth pour trouver des dispositifs</string>
|
||||
<string name="discovery_successfully_bonded">Correctement lié à %1$s</string>
|
||||
<string name="discovery_pair_title">Appairer avec %1$s</string>
|
||||
<string name="discovery_pair_question">Sélectionnez Appairer pour appairer vos dispositifs. Si cela échoue, essayez à nouveau sans appairage.</string>
|
||||
<string name="discovery_yes_pair">Appairage</string>
|
||||
<string name="discovery_dont_pair">Ne pas appairer</string>
|
||||
</resources>
|
||||
|
@ -13,8 +13,12 @@
|
||||
<string name="controlcenter_delete_device">Rimuovi dispositivo</string>
|
||||
<string name="controlcenter_delete_device_name">Rimuovi %1$s</string>
|
||||
<string name="controlcenter_delete_device_dialogmessage">Il dispositivo verrà rimosso e tutti i dati ad esso associati verranno cancellati!</string>
|
||||
<string name="controlcenter_navigation_drawer_open">Apri menu</string>
|
||||
<string name="controlcenter_navigation_drawer_close">Chiudi menu</string>
|
||||
<string name="controlcenter_snackbar_need_longpress">Pressione prolungata sulla scheda per scollegare</string>
|
||||
<string name="controlcenter_snackbar_disconnecting">Disconnessione</string>
|
||||
<string name="controlcenter_snackbar_connecting">Connessione</string>
|
||||
<string name="controlcenter_snackbar_requested_screenshot">Screenshot del dispositivo</string>
|
||||
<string name="title_activity_debug">Debug</string>
|
||||
<!--Strings related to AppManager-->
|
||||
<string name="title_activity_appmanager">Gestione app</string>
|
||||
@ -76,9 +80,11 @@
|
||||
<string name="always">sempre</string>
|
||||
<string name="when_screen_off">se lo schermo è spento</string>
|
||||
<string name="never">mai</string>
|
||||
<string name="pref_header_privacy">Privacy</string>
|
||||
<string name="pref_title_call_privacy_mode">Impostazioni privacy chiamate</string>
|
||||
<string name="pref_call_privacy_mode_off">Mostra nome e numero chiamante</string>
|
||||
<string name="pref_call_privacy_mode_name">Nascondi il nome ma mostra il numero del chiamante</string>
|
||||
<string name="pref_call_privacy_mode_number">Nascondi il numero ma mostra il nome</string>
|
||||
<string name="pref_call_privacy_mode_complete">Nascondi nome e numero del chiamante</string>
|
||||
<string name="pref_blacklist">Blocca applicazioni</string>
|
||||
<string name="pref_header_cannned_messages">Messaggi preimpostati</string>
|
||||
@ -344,7 +350,12 @@
|
||||
<string name="pref_summary_setup_bt_pairing">Disattiva se hai problemi di connession</string>
|
||||
<string name="unit_metric">Metrico</string>
|
||||
<string name="unit_imperial">Imperiale</string>
|
||||
<string name="timeformat_24h">24H</string>
|
||||
<string name="timeformat_am_pm">AM/PM</string>
|
||||
<string name="pref_screen_notification_profile_alarm_clock">Sveglia</string>
|
||||
<string name="find_device_you_found_it">Trovato!</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: Formato dell\'orario</string>
|
||||
<string name="mi2_fw_installhandler_fw53_hint">E\' necessario installare la verione %1$s prima di installare questo firmware!</string>
|
||||
<string name="mi2_enable_text_notifications">Notifiche</string>
|
||||
<string name="off">spento</string>
|
||||
</resources>
|
||||
|
@ -84,6 +84,7 @@
|
||||
<string name="pref_title_call_privacy_mode">通話プライバシーモード</string>
|
||||
<string name="pref_call_privacy_mode_off">名前と番号を表示</string>
|
||||
<string name="pref_call_privacy_mode_name">名前は非表示、番号は表示</string>
|
||||
<string name="pref_call_privacy_mode_number">番号は非表示、名前は表示</string>
|
||||
<string name="pref_call_privacy_mode_complete">名前と番号を非表示</string>
|
||||
<string name="pref_blacklist">アップのブラックリスト</string>
|
||||
<string name="pref_header_cannned_messages">定型のメッセージ</string>
|
||||
@ -267,6 +268,8 @@
|
||||
<string name="updatefirmwareoperation_update_complete_rebooting">ファームウェアのインストールが完了しました。デバイスを再起動します…</string>
|
||||
<string name="updatefirmwareoperation_write_failed">ファームウェアの書き込みに失敗しました</string>
|
||||
<string name="chart_steps">歩数</string>
|
||||
<string name="calories">カロリー</string>
|
||||
<string name="distance">距離</string>
|
||||
<string name="liveactivity_live_activity">生活活動</string>
|
||||
<string name="weeksteps_today_steps_description">今日の歩数、目標: %1$s</string>
|
||||
<string name="pref_title_dont_ack_transfer">活動データ転送に応答しない</string>
|
||||
@ -325,10 +328,12 @@
|
||||
<string name="dbmanagementactivvity_cannot_access_export_path">エクスポート パスにアクセスできません。開発者にご連絡ください。</string>
|
||||
<string name="dbmanagementactivity_exported_to">エクスポートしました: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">DB のエクスポート時にエラー: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_shared">設定のエクスポート時にエラー: %1$s</string>
|
||||
<string name="dbmanagementactivity_import_data_title">データをインポートしますか?</string>
|
||||
<string name="dbmanagementactivity_overwrite_database_confirmation">現在のデータベースを上書きしてもよろしいですか? 現在の活動データは (もしあれば) すべて失われます。</string>
|
||||
<string name="dbmanagementactivity_import_successful">インポートに成功しました。</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">DB のインポート時にエラー: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_importing_shared">設定のインポート時にエラー: %1$s</string>
|
||||
<string name="dbmanagementactivity_delete_activity_data_title">活動データを削除しますか?</string>
|
||||
<string name="dbmanagementactivity_really_delete_entire_db">データベース全体を削除してもよろしいですか? 活動データとお使いのデバイスに関する情報がすべて失われます。</string>
|
||||
<string name="dbmanagementactivity_database_successfully_deleted">データを正常に削除しました。</string>
|
||||
@ -356,4 +361,17 @@
|
||||
<string name="StringUtils_sender"> (%1$s)</string>
|
||||
<string name="find_device_you_found_it">見つかりました!</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: 時刻形式</string>
|
||||
<string name="mi2_fw_installhandler_fw53_hint">このファームウェアをインストールする前に、バージョン %1$s をインストールする必要があります!</string>
|
||||
<string name="mi2_enable_text_notifications">テキスト通知</string>
|
||||
<string name="mi2_enable_text_notifications_summary"><![CDATA[ファームウェア >= 1.0.1.28 と Mili_pro.ft* をインストールしていることが必要です。]]></string>
|
||||
<string name="off">オフ</string>
|
||||
<string name="discovery_attempting_to_pair">%1$s とペアを試みています</string>
|
||||
<string name="discovery_bonding_failed_immediately">%1$s との接続がすぐに失敗しました。</string>
|
||||
<string name="discovery_trying_to_connect_to">接続の試行中: %1$s</string>
|
||||
<string name="discovery_enable_bluetooth">デバイスを見つけるため Bluetooth を有効にします。</string>
|
||||
<string name="discovery_successfully_bonded">%1$s と接続しました。</string>
|
||||
<string name="discovery_pair_title">%1$sとペアにしますか?</string>
|
||||
<string name="discovery_pair_question">お使いのデバイスとペアにする相手を選択します。 これに失敗した場合は、ペア設定をせずに再試行してください。</string>
|
||||
<string name="discovery_yes_pair">ペア</string>
|
||||
<string name="discovery_dont_pair">ペアにしない</string>
|
||||
</resources>
|
||||
|
@ -289,7 +289,7 @@
|
||||
<string name="FetchActivityOperation_about_to_transfer_since">Sobre para transferir dados desde %1$s</string>
|
||||
<string name="waiting_for_reconnect">aguarde para reconectar</string>
|
||||
<string name="activity_prefs_about_you">Sobre você</string>
|
||||
<string name="activity_prefs_year_birth">Ano de aniversário</string>
|
||||
<string name="activity_prefs_year_birth">Ano de anoversário</string>
|
||||
<string name="activity_prefs_gender">Gênero</string>
|
||||
<string name="activity_prefs_height_cm">Altura em cm</string>
|
||||
<string name="activity_prefs_weight_kg">Peso em kg</string>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Gadgetbridge</string>
|
||||
<string name="title_activity_controlcenter">Gadgetbridge</string>
|
||||
<string name="action_settings">Configurações</string>
|
||||
<string name="action_debug">Depurar</string>
|
||||
<string name="action_quit">Sair</string>
|
||||
@ -11,6 +13,12 @@
|
||||
<string name="controlcenter_delete_device">Apagar dispositivo</string>
|
||||
<string name="controlcenter_delete_device_name">Apagar %1$s</string>
|
||||
<string name="controlcenter_delete_device_dialogmessage">Isto irá apagar o dispositivo e apagar os dados associados!</string>
|
||||
<string name="controlcenter_navigation_drawer_open">Abrir gaveta de navegação</string>
|
||||
<string name="controlcenter_navigation_drawer_close">Fechar gaveta de navegação</string>
|
||||
<string name="controlcenter_snackbar_need_longpress">Pressione o cartão longamente para desligar</string>
|
||||
<string name="controlcenter_snackbar_disconnecting">A desligar</string>
|
||||
<string name="controlcenter_snackbar_connecting">A ligar</string>
|
||||
<string name="controlcenter_snackbar_requested_screenshot">Capturar o ecrã do dispositivo</string>
|
||||
<string name="title_activity_debug">Depurar</string>
|
||||
<!--Strings related to AppManager-->
|
||||
<string name="title_activity_appmanager">Administrador de App</string>
|
||||
@ -20,12 +28,12 @@
|
||||
<string name="appmananger_app_delete">Apagar</string>
|
||||
<string name="appmananger_app_delete_cache">Apagar e remover do cache</string>
|
||||
<string name="appmananger_app_reinstall">Reinstalar</string>
|
||||
<string name="appmanager_app_openinstore">Procurar na loja Pebble</string>
|
||||
<string name="appmanager_app_openinstore">Buscar na loja Pebble</string>
|
||||
<string name="appmanager_health_activate">Ativar</string>
|
||||
<string name="appmanager_health_deactivate">Desativar</string>
|
||||
<string name="appmanager_hrm_activate">Ativar HRM</string>
|
||||
<string name="appmanager_hrm_deactivate">Desativar HRM</string>
|
||||
<string name="appmanager_weather_activate">Ativar app de clima do sistema</string>
|
||||
<string name="appmanager_weather_activate">Ativas app de clima do sistema</string>
|
||||
<string name="appmanager_weather_deactivate">Desativar app de clima do sistema</string>
|
||||
<string name="appmanager_weather_install_provider">Instalar notificações do app de clima</string>
|
||||
<string name="app_configure">Configurar</string>
|
||||
@ -35,7 +43,10 @@
|
||||
<!--Strings related to FwAppInstaller-->
|
||||
<string name="title_activity_fw_app_insaller">Instalador FW/App</string>
|
||||
<string name="fw_upgrade_notice">Está prestes a instalar o firmware %s no lugar do atual na sua Mi Band.</string>
|
||||
<string name="fw_multi_upgrade_notice">Estás prestes a instalar os firmwares %1$s e %2$s em vez dos que estão atualmente na sua Mi Band.</string>
|
||||
<string name="miband_firmware_known">O firmware foi testado e é compatível com o Gadgetbridge.</string>
|
||||
<string name="miband_firmware_unknown_warning">Este firmware não foi testado e pode não ser compatível com o Gadgetbridge.\n\nÉ preferível que NÃO o instale na sua Mi Band!</string>
|
||||
<string name="miband_firmware_suggest_whitelist">Se ainda assim quiser continuar e tudo continuar a funcionar normalmente, por favor diga aos programadores do Gadgetbridge para permitir o firmware versão: %s</string>
|
||||
<!--Strings related to Settings-->
|
||||
<string name="title_activity_settings">Configurações</string>
|
||||
<string name="pref_header_general">Configurações Gerais</string>
|
||||
@ -46,6 +57,7 @@
|
||||
<string name="pref_default">Padrão</string>
|
||||
<string name="pref_header_datetime">Data e Hora</string>
|
||||
<string name="pref_title_datetime_syctimeonconnect">Sincronizar hora</string>
|
||||
<string name="pref_summary_datetime_syctimeonconnect">Sincronizar a hora para o dispositivo a quando da ligação e quando a hora ou fuso horário se alterarem no Android</string>
|
||||
<string name="pref_title_theme">Tema</string>
|
||||
<string name="pref_theme_light">Claro</string>
|
||||
<string name="pref_theme_dark">Escuro</string>
|
||||
@ -61,8 +73,10 @@
|
||||
<string name="pref_summary_notifications_pebblemsg">Suportar notificações de aplicações que enviam notificações pelo PebbleKit.</string>
|
||||
<string name="pref_title_notifications_generic">Suportar notificações genéricas</string>
|
||||
<string name="pref_title_whenscreenon">... e quando o ecrã estiver ligado</string>
|
||||
<string name="pref_title_notification_filter">Não perturbe</string>
|
||||
<string name="pref_summary_notification_filter">Ignorar notificações indesejadas enquanto estiver no modo Não Perturbe.</string>
|
||||
<string name="pref_title_notification_filter">Não incomodar</string>
|
||||
<string name="pref_summary_notification_filter">Ignorar notificações indesejadas enquanto estiver no modo Não Incomodar.</string>
|
||||
<string name="pref_title_transliteration">Transliteração</string>
|
||||
<string name="pref_summary_transliteration">Ativar apenas se o seu dispositivo não suportar a sua caracteres da sua língua (Atualmente apenas Cirílico)</string>
|
||||
<string name="always">sempre</string>
|
||||
<string name="when_screen_off">quando o ecrã estiver desligado</string>
|
||||
<string name="never">nunca</string>
|
||||
@ -70,6 +84,7 @@
|
||||
<string name="pref_title_call_privacy_mode">Modo de chamada privada</string>
|
||||
<string name="pref_call_privacy_mode_off">Exibir nome e número</string>
|
||||
<string name="pref_call_privacy_mode_name">Ocultar nome e exibir número</string>
|
||||
<string name="pref_call_privacy_mode_number">Ocular o número e exibir o nome</string>
|
||||
<string name="pref_call_privacy_mode_complete">Ocultar nome e número</string>
|
||||
<string name="pref_blacklist">Aplicações ignoradas</string>
|
||||
<string name="pref_header_cannned_messages">Histórico de mensagens</string>
|
||||
@ -85,14 +100,17 @@
|
||||
<string name="pref_title_pebble_sync_health">Sincronizar com Pebble Health</string>
|
||||
<string name="pref_title_pebble_sync_misfit">Sincronizar com Misfit</string>
|
||||
<string name="pref_title_pebble_sync_morpheuz">Sincronizar com Morpheuz</string>
|
||||
<string name="pref_title_enable_outgoing_call">Suportar chamadas efetuadas</string>
|
||||
<string name="pref_summary_enable_outgoing_call">Desactivar isto também irá fazer com que o Pebble 2/LE não vibre durante as chamadas efetuadas</string>
|
||||
<string name="pref_title_enable_pebblekit">Permitir acesso por Apps Android de terceiros.</string>
|
||||
<string name="pref_summary_enable_pebblekit">Ativar suporte experimental a Apps Android que usem PebbleKit</string>
|
||||
<string name="pref_title_sunrise_sunset">Despertar e pôr do sol</string>
|
||||
<string name="pref_summary_sunrise_sunset">Enviar despertar e pôr do sol com base na localização do pebble</string>
|
||||
<string name="pref_summary_sunrise_sunset">Enviar despertar e pôr do sol baseado na localização do Pebble</string>
|
||||
<string name="pref_title_autoremove_notifications">Auto remover notificações rejeitadas</string>
|
||||
<string name="pref_summary_autoremove_notifications">Notificações são automaticamente removidas quando rejeitadas no Android</string>
|
||||
<string name="pref_title_pebble_privacy_mode">Modo de privacidade</string>
|
||||
<string name="pref_pebble_privacy_mode_off">Notificações</string>
|
||||
<string name="pref_pebble_privacy_mode_content">Deslocar texto de notificações que extravasar o ecrã </string>
|
||||
<string name="pref_pebble_privacy_mode_content">Deslocar texto de notificações que extravasar o ecrã</string>
|
||||
<string name="pref_pebble_privacy_mode_complete">Apenas mostrar ícone de notificações</string>
|
||||
<string name="pref_header_location">Localização</string>
|
||||
<string name="pref_title_location_aquire">Obter localização</string>
|
||||
@ -106,42 +124,88 @@
|
||||
<string name="pref_summary_pebble_forceprotocol">Esta opção força o uso do protocolo de notificação mais recente. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER!</string>
|
||||
<string name="pref_title_pebble_forceuntested">Permitir recursos não certificados</string>
|
||||
<string name="pref_summary_pebble_forceuntested">Permitir recursos não certificados. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER!</string>
|
||||
<string name="pref_title_pebble_forcele">Preferir sempre BLE</string>
|
||||
<string name="pref_title_pebble_forcele">Sempre preferir BLE</string>
|
||||
<string name="pref_summary_pebble_forcele">Utilizar suporte para Pebble LE em todos os Pebbles em vez do clássico BT. Necessita que se emparelhe um \"Pebble LE\" depois de dispositivos não LE se terem ligado uma vez</string>
|
||||
<string name="pref_title_pebble_mtu_limit">Pebble 2/LE GATT MTU limite</string>
|
||||
<string name="pref_summary_pebble_mtu_limit">Se o seu Pebble2/Pebble LE não funciona como esperado, ative esta opção para limitar o tamanho das transferências (intervalo válido é 20-512)</string>
|
||||
<string name="pref_title_pebble_enable_applogs">Activar Registo da Aplicação do Dispositivo</string>
|
||||
<string name="pref_summary_pebble_enable_applogs">Causa que os registos das aplicações do relógio sejam guardados pelo Gadgetbridge (necessita de religação)</string>
|
||||
<string name="pref_title_pebble_always_ack_pebblekit">Antecipar confirmações do PebbleKit</string>
|
||||
<string name="pref_summary_pebble_always_ack_pebblekit">Irá fazer com que as mensagens enviadas para aplicações de terceiros sejam sempre imediatamente confirmadas</string>
|
||||
<string name="pref_title_pebble_reconnect_attempts">Tentativas de Ligação</string>
|
||||
<string name="pref_title_unit_system">Unidades</string>
|
||||
<string name="pref_title_timeformat">Formato da hora</string>
|
||||
<string name="pref_title_screentime">Duração do ecrã</string>
|
||||
<string name="prefs_title_all_day_heart_rate">Medição Contínua de Ritmo Cardíaco</string>
|
||||
<string name="preferences_hplus_settings">Configurações HPlus/Makibes</string>
|
||||
<string name="not_connected">desligado</string>
|
||||
<string name="connecting">a ligar</string>
|
||||
<string name="connected">ligado</string>
|
||||
<string name="unknown_state">estado desconhecido</string>
|
||||
<string name="connectionstate_hw_fw">HW: %1$s FW: %2$s</string>
|
||||
<string name="connectionstate_fw">FW: %1$s</string>
|
||||
<string name="_unknown_">(desconhecido)</string>
|
||||
<string name="test">Teste</string>
|
||||
<string name="test_notification">Teste de notificações</string>
|
||||
<string name="test_notification">Teste de notificação</string>
|
||||
<string name="this_is_a_test_notification_from_gadgetbridge">Isto é uma notificação de teste do Gadgetbridge</string>
|
||||
<string name="bluetooth_is_not_supported_">Bluetooth não suportado</string>
|
||||
<string name="bluetooth_is_disabled_">Bluetooth desligado</string>
|
||||
<string name="tap_connected_device_for_app_mananger">Toque num dispositivo ligado para Gerir Aplicações</string>
|
||||
<string name="tap_connected_device_for_activity">Toque num dispositivo ligado para ver a Atividade</string>
|
||||
<string name="tap_connected_device_for_vibration">Toque num dispositivo ligado para o fazer Vibrar</string>
|
||||
<string name="tap_a_device_to_connect">Toque num dispositivo para ligar</string>
|
||||
<string name="cannot_connect_bt_address_invalid_">Não foi possível ligar. Endereço Bluetooth inválido?</string>
|
||||
<string name="gadgetbridge_running">Gadgetbridge a executar</string>
|
||||
<string name="installing_binary_d_d">A instalar o binário %1$d/%2$d</string>
|
||||
<string name="installation_failed_">falha na instalação!</string>
|
||||
<string name="installation_successful">instalação bem sucedida</string>
|
||||
<string name="firmware_install_warning">ESTÁ A TENTAR INSTALAR UM FIRMWARE, CONTINUE À SUA RESPONSABILIDADE.\n\n\n Este firmware é para a Versão de HW: %s</string>
|
||||
<string name="app_install_info">Está prestes a instalar a aplicação seguinte::\n\n\n%1$s Versão %2$s por %3$s\n</string>
|
||||
<string name="n_a">N/D</string>
|
||||
<string name="initialized">inicializado</string>
|
||||
<string name="title_activity_discovery">Dispositivo encontrado</string>
|
||||
<string name="appversion_by_creator">%1$s por %2$s</string>
|
||||
<string name="title_activity_discovery">Procurar Dispositivos</string>
|
||||
<string name="discovery_stop_scanning">Terminar a procura</string>
|
||||
<string name="discovery_start_scanning">Iniciar a procura</string>
|
||||
<string name="action_discover">Ligue o novo dispositivo</string>
|
||||
<string name="device_with_rssi">%1$s (%2$s)</string>
|
||||
<string name="title_activity_android_pairing">Emparelhar dispositivo</string>
|
||||
<string name="android_pairing_hint">Use o diálogo do sistema Android para emparelhar com o dispositivo</string>
|
||||
<string name="title_activity_mi_band_pairing">Emparelhar com a sua Mi Band</string>
|
||||
<string name="pairing">A emparelhar com %s...</string>
|
||||
<string name="pairing_creating_bond_with">A criar ligação com %1$s (%2$s)</string>
|
||||
<string name="pairing_unable_to_pair_with">Não foi possível emparelhar com %1$s (%2$s)</string>
|
||||
<string name="pairing_in_progress">Associação em progresso: %1$s (%2$s)</string>
|
||||
<string name="pairing_already_bonded">Já associado com %1$s (%2$s), a ligar...</string>
|
||||
<string name="message_cannot_pair_no_mac">Nenhum endereço MAC fornecido, não é possível emparelhar.</string>
|
||||
<string name="preferences_category_device_specific_settings">Configurações Específicas do Dispositivo</string>
|
||||
<string name="preferences_miband_settings">Configurações Mi Band</string>
|
||||
<string name="male">masculino</string>
|
||||
<string name="female">feminino</string>
|
||||
<string name="other">outro</string>
|
||||
<string name="left">esquerda</string>
|
||||
<string name="right">direita</string>
|
||||
<string name="miband_pairing_using_dummy_userdata">Sem dados de utilizador. Por enquanto serão usados dados fictícios</string>
|
||||
<string name="miband_pairing_tap_hint">Quando a sia Mi Band vibrar e piscar, dê-lhe alguns toques seguidos.</string>
|
||||
<string name="appinstaller_install">Instalar</string>
|
||||
<string name="discovery_connected_devices_hint">Torne o seu dispositivo visível. É improvável que os dispositivos atualmente ligados sejam apresentados. Ative a localização (i.e, GPS) no Android 6+. Desative também a Proteção de Privacidade para o Gadgetbridge, pois esta poderá bloquear e levar ao reinício do seu telefone. Se depois de alguns minutos não for encontrado qualquer dispositivo, tente outra vez após reiniciar o telefone.</string>
|
||||
<string name="discovery_note">Nota:</string>
|
||||
<string name="candidate_item_device_image">Imagem do Dispositivo</string>
|
||||
<string name="miband_prefs_alias">Nome/Apelido</string>
|
||||
<string name="pref_header_vibration_count">Quantidade de vibrações</string>
|
||||
<string name="title_activity_sleepmonitor">Monitor de sono</string>
|
||||
<string name="pref_write_logfiles">Escrever arquivos de registo</string>
|
||||
<string name="initializing">Inicializando</string>
|
||||
<string name="busy_task_fetch_activity_data">A Descarregar Dados de Atividade</string>
|
||||
<string name="sleep_activity_date_range">De %1$s até %2$s</string>
|
||||
<string name="miband_prefs_wearside">De que lado a usa?</string>
|
||||
<string name="pref_screen_vibration_profile">Perfil de Vibração</string>
|
||||
<string name="vibration_profile_staccato">Destacado</string>
|
||||
<string name="vibration_profile_short">Pequeno</string>
|
||||
<string name="vibration_profile_medium">Médio</string>
|
||||
<string name="vibration_profile_long">Longo</string>
|
||||
<string name="vibration_profile_waterdrop">Gota de Água</string>
|
||||
<string name="vibration_profile_ring">Toque</string>
|
||||
<string name="vibration_profile_alarm_clock">Alarme</string>
|
||||
<string name="miband_prefs_vibration">Vibração</string>
|
||||
<string name="vibration_try">Tente</string>
|
||||
@ -153,6 +217,8 @@
|
||||
<string name="pref_screen_notification_profile_generic_chat">Conversas</string>
|
||||
<string name="pref_screen_notification_profile_generic_navigation">Navegação</string>
|
||||
<string name="pref_screen_notification_profile_generic_social">Rede social</string>
|
||||
<string name="control_center_find_lost_device">Encontrar dispositivo perdido</string>
|
||||
<string name="control_center_cancel_to_stop_vibration">Cancele para parar a vibração.</string>
|
||||
<string name="title_activity_charts">A sua atividade</string>
|
||||
<string name="title_activity_set_alarm">Configurar Alarmes</string>
|
||||
<string name="controlcenter_start_configure_alarms">Configurar alarmes</string>
|
||||
@ -168,28 +234,52 @@
|
||||
<string name="user_feedback_miband_set_alarms_failed">Existiu um erro ao definir o alarme, tente novamente!</string>
|
||||
<string name="user_feedback_miband_set_alarms_ok">Alarme enviado para o dispositivo!</string>
|
||||
<string name="chart_no_data_synchronize">Sem data. Sincronizar com o dispositivo?</string>
|
||||
<string name="user_feedback_miband_activity_data_transfer">Prestes a transferir %1$s de dados desde %2$s</string>
|
||||
<string name="miband_prefs_fitness_goal">Objetivo de passos por dia</string>
|
||||
<string name="dbaccess_error_executing">Erro ao executar \'%1$s\'</string>
|
||||
<string name="controlcenter_start_activitymonitor">A Sua Atividade (ALPHA)</string>
|
||||
<string name="cannot_connect">Não foi possível ligar: %1$s</string>
|
||||
<string name="installer_activity_unable_to_find_handler">Não foi possível encontrar um manipulador para instalar o arquivo.</string>
|
||||
<string name="pbw_install_handler_unable_to_install">Não foi possível instalar o ficheiro: %1$s</string>
|
||||
<string name="pbw_install_handler_hw_revision_mismatch">Não foi possível instalar o firmware fornecido: ele não corresponde à versão de hardware do Pebble</string>
|
||||
<string name="installer_activity_wait_while_determining_status">Aguarde enquanto se determina o estado da instalação...</string>
|
||||
<string name="notif_battery_low_title">Dispositivo com bateria baixa!</string>
|
||||
<string name="notif_battery_low_percent">%1$s bateria restante: %2$s%%</string>
|
||||
<string name="notif_battery_low_bigtext_last_charge_time">Última carga: %s \n</string>
|
||||
<string name="notif_battery_low_bigtext_number_of_charges">Número de cargas: %s</string>
|
||||
<string name="sleepchart_your_sleep">O Seu Sono</string>
|
||||
<string name="weeksleepchart_sleep_a_week">Sono durante a semana</string>
|
||||
<string name="weeksleepchart_today_sleep_description">Sono hoje, objetivo: %1$s</string>
|
||||
<string name="weekstepschart_steps_a_week">Passos na semana</string>
|
||||
<string name="activity_sleepchart_activity_and_sleep">A Sua Atividade e Sono</string>
|
||||
<string name="updating_firmware">Atualizando Firmware...</string>
|
||||
<string name="fwapp_install_device_not_ready">Arquivo não pode ser instalado, o dispositivo não está pronto.</string>
|
||||
<string name="miband_installhandler_miband_firmware">Firmware da Mi Band %1$s</string>
|
||||
<string name="miband_fwinstaller_compatible_version">Versão compatível</string>
|
||||
<string name="miband_fwinstaller_untested_version">Versão não testada!</string>
|
||||
<string name="fwappinstaller_connection_state">Ligação do Dispositivo: %1$s</string>
|
||||
<string name="pbw_installhandler_pebble_firmware">Firmware da Pebble: %1$s</string>
|
||||
<string name="pbwinstallhandler_correct_hw_revision">Revisão de hardware correta</string>
|
||||
<string name="pbwinstallhandler_incorrect_hw_revision">Revisão de hardware inválida!</string>
|
||||
<string name="pbwinstallhandler_app_item">%1$s (%2$s)</string>
|
||||
<string name="updatefirmwareoperation_updateproblem_do_not_reboot">Ocorreu um erro com a transferência do firmware. NÃO REINICIE a sua Mi Band!</string>
|
||||
<string name="updatefirmwareoperation_metadata_updateproblem">Problemas ao transferir os metadados do firmware</string>
|
||||
<string name="updatefirmwareoperation_update_complete">Instalação do Firmware completa</string>
|
||||
<string name="updatefirmwareoperation_update_complete_rebooting">Instalação do Firmware completa, reiniciando o dispositivo...</string>
|
||||
<string name="updatefirmwareoperation_write_failed">Falha ao instalar o Firmware</string>
|
||||
<string name="chart_steps">Passos</string>
|
||||
<string name="liveactivity_live_activity">Atividade em Tempo Real</string>
|
||||
<string name="liveactivity_live_activity">Atividade em tempo real</string>
|
||||
<string name="weeksteps_today_steps_description">Passos hoje, objetivo: %1$s</string>
|
||||
<string name="pref_title_dont_ack_transfer">Não confirmar a transferência de dados de atividade</string>
|
||||
<string name="pref_summary_dont_ack_transfers">Se os dados de atividade não forem confirmados pela banda, eles não serão apagados. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
|
||||
<string name="pref_summary_keep_data_on_device">Irá manter os dados na Mi Band mesmo após a sincronização. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
|
||||
<string name="pref_title_low_latency_fw_update">Usar modo de baixa latência para atualizações de FW</string>
|
||||
<string name="pref_summary_low_latency_fw_update">Isto pode ajudar em dispositivos onde as atualizações de firmware falhem</string>
|
||||
<string name="live_activity_steps_history">Histórico de passos</string>
|
||||
<string name="live_activity_current_steps_per_minute">Passos/min atuais</string>
|
||||
<string name="live_activity_total_steps">Total de passos</string>
|
||||
<string name="live_activity_steps_per_minute_history">Histórico de passos por minuto</string>
|
||||
<string name="live_activity_start_your_activity">Iniciar a sua atividade</string>
|
||||
<string name="live_activity_start_your_activity">Iniciar sua atividade</string>
|
||||
<string name="abstract_chart_fragment_kind_activity">Atividade</string>
|
||||
<string name="abstract_chart_fragment_kind_light_sleep">Sono leve</string>
|
||||
<string name="abstract_chart_fragment_kind_deep_sleep">Sono profundo</string>
|
||||
@ -200,7 +290,13 @@
|
||||
<string name="miband_fwinstaller_incompatible_version">Firmware incompatível</string>
|
||||
<string name="fwinstaller_firmware_not_compatible_to_device">Este firmware não é compatível com seu dispositivo</string>
|
||||
<string name="miband_prefs_reserve_alarm_calendar">Alarmes reservados para eventos próximos</string>
|
||||
<string name="miband_prefs_hr_sleep_detection">Utilizar sensor de Ritmo Cardíaco para melhorar a deteção de sono</string>
|
||||
<string name="miband_prefs_device_time_offset_hours">Compensação da hora do dispositivo em horas (para detetar o sono de trabalhadores por turnos)</string>
|
||||
<string name="miband2_prefs_dateformat">Mi2: Formato da Data</string>
|
||||
<string name="dateformat_time">Hora</string>
|
||||
<string name="dateformat_date_time"><![CDATA[Hora & Data]]></string>
|
||||
<string name="mi2_prefs_activate_display_on_lift">Ativar ecrã do dispositivo quando o levantar</string>
|
||||
<string name="FetchActivityOperation_about_to_transfer_since">Prestes a transferir dados desde %1$s</string>
|
||||
<string name="waiting_for_reconnect">aguarde para tornar a ligar</string>
|
||||
<string name="activity_prefs_about_you">Sobre você</string>
|
||||
<string name="activity_prefs_year_birth">Ano de nascimento</string>
|
||||
@ -209,12 +305,39 @@
|
||||
<string name="activity_prefs_weight_kg">Peso em kg</string>
|
||||
<string name="authenticating">a autenticar</string>
|
||||
<string name="authentication_required">autenticação necessária</string>
|
||||
<string name="appwidget_text">Zzz</string>
|
||||
<string name="add_widget">Adicionar widget</string>
|
||||
<string name="activity_prefs_sleep_duration">Preferir duração do sono em horas</string>
|
||||
<string name="activity_prefs_sleep_duration">Preferir duração de sono em horas</string>
|
||||
<string name="appwidget_alarms_set">Alarme definido para as %1$02d:%2$02d</string>
|
||||
<string name="device_hw">HW: %1$s</string>
|
||||
<string name="device_fw">FW: %1$s</string>
|
||||
<string name="error_creating_directory_for_logfiles">Erro ao criar o diretório para os ficheiros de registo: %1$s</string>
|
||||
<string name="DEVINFO_HR_VER">RH:</string>
|
||||
<string name="updatefirmwareoperation_update_in_progress">Atualização de Firmware em curso</string>
|
||||
<string name="updatefirmwareoperation_firmware_not_sent">Firmware não enviado</string>
|
||||
<string name="charts_legend_heartrate">Ritmo Cardíaco</string>
|
||||
<string name="live_activity_heart_rate">Ritmo Cardíaco</string>
|
||||
<string name="pref_title_pebble_health_store_raw">Armazenar registo não processado na base de dados.</string>
|
||||
<string name="pref_summary_pebble_health_store_raw">Se ativado os dados são guardados no seu formato original, estando mais tarde disponíveis para interpretação. Nota: neste caso a base de dados será maior!</string>
|
||||
<string name="action_db_management">Gestão da Base de Dados</string>
|
||||
<string name="title_activity_db_management">Gestão da Base de Dados</string>
|
||||
<string name="activity_db_management_import_export_explanation">As operações sobre a base de dados utilizam o seguinte caminho no seu dispositivo. \nEste caminho está acessível a outras aplicações Android e ao seu computador. \nEspere encontrar a sua base de dados exportada (ou a base de dados que pretende importar) nessa localização:</string>
|
||||
<string name="activity_db_management_merge_old_title">Apagar Base de Dados Antiga</string>
|
||||
<string name="dbmanagementactivvity_cannot_access_export_path">Não foi possível aceder ao caminho de exportação. Por favor contacte os programadores.</string>
|
||||
<string name="dbmanagementactivity_exported_to">Exportado para: %1$s</string>
|
||||
<string name="dbmanagementactivity_error_exporting_db">Erro ao exportar a Base de Dados: %1$s</string>
|
||||
<string name="dbmanagementactivity_import_data_title">Importar dados?</string>
|
||||
<string name="dbmanagementactivity_import_successful">Dados importados com sucesso.</string>
|
||||
<string name="dbmanagementactivity_overwrite_database_confirmation">Realmente deseja sobrescrever a base de dados atual? Todos os dados de atividade existentes irão perder-se. </string>
|
||||
<string name="dbmanagementactivity_import_successful">Importação com sucesso.</string>
|
||||
<string name="dbmanagementactivity_error_importing_db">Erro ao importar a Base de Dados: %1$s</string>
|
||||
<string name="dbmanagementactivity_delete_activity_data_title">Apagar os dados de atividade?</string>
|
||||
<string name="dbmanagementactivity_really_delete_entire_db">Apagar realmente toda a base de dados? Todos os seus dados de atividade e informação sobre os seus dispositivos irá perder-se.</string>
|
||||
<string name="dbmanagementactivity_database_successfully_deleted">Dados apagados com sucesso.</string>
|
||||
<string name="dbmanagementactivity_db_deletion_failed">Não foi possível apagar a base de dados.</string>
|
||||
<string name="dbmanagementactivity_delete_old_activity_db">Apagar os dados de atividade antigos?</string>
|
||||
<string name="dbmanagementactivity_delete_old_activitydb_confirmation">Apagar realmente a base de dados antiga? Dados de atividade não importados irão perder-se.</string>
|
||||
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">Atividade antiga foi apagada com sucesso.</string>
|
||||
<string name="dbmanagementactivity_old_activity_db_deletion_failed">Não foi possível apagar a atividade antiga da base de dados.</string>
|
||||
<string name="dbmanagementactivity_overwrite">Escrever por cima</string>
|
||||
<string name="Cancel">Cancelar</string>
|
||||
<string name="Delete">Apagar</string>
|
||||
@ -222,7 +345,20 @@
|
||||
<string name="title_activity_vibration">Vibração</string>
|
||||
<!--Strings related to Pebble Pairing Activity-->
|
||||
<string name="title_activity_pebble_pairing">A emparelhar Pebble</string>
|
||||
<string name="pebble_pairing_hint">É esperado que veja uma notificação de emparelhamento no seu dispositivo Android. Se isso não acontecer, aceda às notificações e aceite o pedido de emparelhamento. Depois aceite igualmente o pedido de emparelhamento no seu Pebble</string>
|
||||
<string name="weather_notification_label">Garanta que este tema se encontra ativado na Aplicação de Meteorologia para obter informação meteorológica atualizada no seu Pebble.\n\nNão é necessária qualquer configuração aqui.\n\nPode ativar a aplicação nativa de meteorologia do seu Pebble através da gestão de aplicações.\n\nTemas de relógio irão apresentar a informação meteorológica automaticamente.</string>
|
||||
<string name="pref_title_setup_bt_pairing">Ativar o emparelhamento Bluetooth</string>
|
||||
<string name="pref_summary_setup_bt_pairing">Desative isto caso tenha problemas na ligação</string>
|
||||
<string name="unit_metric">Métrico</string>
|
||||
<string name="unit_imperial">Imperial</string>
|
||||
<string name="timeformat_24h">24H</string>
|
||||
<string name="timeformat_am_pm">Manhã/Tarde</string>
|
||||
<string name="pref_screen_notification_profile_alarm_clock">Alarme</string>
|
||||
<string name="StringUtils_sender">(%1$s)</string>
|
||||
<string name="find_device_you_found_it">Encontrado!</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: Formato da hora</string>
|
||||
<string name="mi2_fw_installhandler_fw53_hint">Deve instalar a versão %1$s antes de instalar este firmware!</string>
|
||||
<string name="mi2_enable_text_notifications">Notificações de texto</string>
|
||||
<string name="mi2_enable_text_notifications_summary"><![CDATA[Necessita de firmware >= 1.0.1.28 e Mili_pro.ft* instalado.]]></string>
|
||||
<string name="off">desligado</string>
|
||||
</resources>
|
||||
|
@ -1,9 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<changelog>
|
||||
<release version="next">
|
||||
<release
|
||||
version="0.18.5"
|
||||
versioncode="92">
|
||||
<change>Applied some material design guidelines to Charts and (pebble) app management
|
||||
</change>
|
||||
<change>Pebble: improve compatiblity with watchapp configuration pages</change>
|
||||
<change>Changed colours: deep sleep is now dark blue, light sleep is now light blue</change>
|
||||
<change>Support for exporting and importing of preferences in addition to the database
|
||||
</change>
|
||||
<change>Visual improvements of the pie charts</change>
|
||||
<change>Add filter by name in the App blacklist activity</change>
|
||||
<change>Pebble: improve compatibility with watch app configuration pages</change>
|
||||
<change>Pebble: display battery percentage (will only update once an hour)</change>
|
||||
<change>HPlus: users can now decide whether they want to pair the device or not, hopefully
|
||||
fixing some connection problems (#642)
|
||||
</change>
|
||||
<change>HPlus: display battery state and warn on low battery</change>
|
||||
</release>
|
||||
|
||||
<release version="0.18.4" versioncode="91">
|
||||
|
@ -82,6 +82,7 @@ public class EntitiesTest extends TestBase {
|
||||
@Test
|
||||
public void testDBHelper() {
|
||||
GBDevice dummyGBDevice = createDummyGDevice("00:00:00:00:01");
|
||||
dummyGBDevice.setState(GBDevice.State.INITIALIZED);
|
||||
Device device = DBHelper.getDevice(dummyGBDevice, daoSession);
|
||||
assertNotNull(device);
|
||||
assertEquals("00:00:00:00:01", device.getIdentifier());
|
||||
@ -178,6 +179,8 @@ public class EntitiesTest extends TestBase {
|
||||
public void testDeviceAttributes() throws Exception {
|
||||
GBDevice dummyGBDevice = createDummyGDevice("00:00:00:00:02");
|
||||
dummyGBDevice.setFirmwareVersion("1.0");
|
||||
dummyGBDevice.setState(GBDevice.State.INITIALIZED);
|
||||
|
||||
Device deviceOld = DBHelper.getDevice(dummyGBDevice, daoSession);
|
||||
assertNotNull(deviceOld);
|
||||
|
||||
|
@ -44,11 +44,6 @@ class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pair() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
|
||||
|
@ -37,13 +37,24 @@ public class LanguageUtilsTest extends TestBase {
|
||||
|
||||
@Test
|
||||
public void testTransliterateOption() throws Exception {
|
||||
setDefaultTransliteration();
|
||||
assertFalse("Transliteration option fail! Expected 'Off' by default, but result is 'On'", LanguageUtils.transliterate());
|
||||
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("transliteration", true);
|
||||
editor.apply();
|
||||
|
||||
enableTransliteration(true);
|
||||
assertTrue("Transliteration option fail! Expected 'On', but result is 'Off'", LanguageUtils.transliterate());
|
||||
}
|
||||
|
||||
private void setDefaultTransliteration() {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove("transliteration");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void enableTransliteration(boolean enable) {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("transliteration", enable);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ public class LoggingTest extends TestBase {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() {
|
||||
assertTrue(FileUtils.deleteRecursively(getLogFilesDir()));
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
|
||||
|
Loading…
Reference in New Issue
Block a user