1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-26 03:46:49 +01:00
This commit is contained in:
Sebastian Kranz 2018-10-01 06:39:08 +02:00
commit 6b82340093
73 changed files with 1746 additions and 1079 deletions

17
.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.java text
*.gradle text
*.md text
*.properties text
*.xml text
# Declare files that will always have CRLF line endings on checkout.
#*.sln text eol=crlf
# Denote all files that are truly binary and should not be modified.
#*.png binary
#*.jpg binary

25
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,25 @@
---
name: Bug report
about: Create a report to help us improve
---
#### Before reporting a bug, please confirm the following:
- [ ] I have read the [wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki), and I didn't find a solution to my problem / an answer to my question.
- [ ] I have searched the [issues](https://github.com/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question.
- [ ] If you upload an image or other content, please make sure you have read and understood the [github policies and terms of services](https://help.github.com/articles/github-terms-of-service/#1-responsibility-for-user-generated-content)
#### Your issue is:
*If possible, please attach [logs](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! that might help identifying the problem.*
#### Your wearable device is:
*Please specify model and firmware version if possible*
#### Your android version is:
#### Your Gadgetbridge version is:
*New issues about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.*

View File

@ -0,0 +1,25 @@
---
name: Feature request
about: Suggest an idea for this project
---
#### Before requesting a new feature, please confirm the following:
- [ ] I have read the [wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki), and I didn't find a solution to my problem / an answer to my question.
- [ ] I have searched the [issues](https://github.com/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question.
- [ ] If you upload an image or other content, please make sure you have read and understood the [github policies and terms of services](https://help.github.com/articles/github-terms-of-service/#1-responsibility-for-user-generated-content)
#### Your issue is:
*If applicable, please attach [logs](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Log-Files)*
#### Your wearable device is:
*Please specify model and firmware version if possible*
#### Your android version is:
#### Your Gadgetbridge version is:
*New requests about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.*

View File

@ -16,7 +16,7 @@ android:
- tools - tools
# The BuildTools version used by your project # The BuildTools version used by your project
- build-tools-27.0.3 - build-tools-28.0.2
# The SDK version used to compile your project # The SDK version used to compile your project
- android-27 - android-27

View File

@ -1,5 +1,23 @@
### Changelog ### Changelog
#### Version 0.30.0
* Amazfit Bip + Mi Band 3: Support for right to left display (configurable) (#976)
* Add Arabic, Bengali Farsi, Persian, Scandinavian transliteration
* Add support for some Roidmi FM receivers
* Mi Band 3: Allow enabling the "Workout" menu item
* Mi Band 3: Support for night mode configuration
* Huami devices: fix seldom activity/sports synchronization problem (#1264)
* Preferences: Make minimum heart rate configurable (lower values will be disregarded)
* Preferences: Configure minimum time between notifications
* Preferences: Group language settings
* Attempt to fix BLE connection issues on Samsung S devices
* Week sleep and steps charts: display balance (actual value vs. desired value)
* Live Activity: show current/maximum heart rate, display minute steps and total steps and more improvements
* Live Activity: fix discrepancy between number of steps in Gadgetbridge and wearable device
* Fix missing caller ID for incoming calls on Android 9
* Support for easy sharing of log files via the Debug screen
* Misc small bugfixes
#### Version 0.29.1 #### Version 0.29.1
* Mi Band 3: Support setting language to to German, Italian, French, Polish, Japanese, Korean (read wiki) * Mi Band 3: Support setting language to to German, Italian, French, Polish, Japanese, Korean (read wiki)
* Mi Band 3: Support flashing latest RES files * Mi Band 3: Support flashing latest RES files

View File

@ -15,6 +15,8 @@ vendor's servers.
[![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge) [![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge)
[![Code Quality: Java](https://img.shields.io/lgtm/grade/java/g/Freeyourgadget/Gadgetbridge.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Freeyourgadget/Gadgetbridge/context:java)
[![Total Alerts](https://img.shields.io/lgtm/alerts/g/Freeyourgadget/Gadgetbridge.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Freeyourgadget/Gadgetbridge/alerts)
## Download ## Download

View File

@ -17,7 +17,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7
} }
compileSdkVersion 27 compileSdkVersion 27
buildToolsVersion "27.0.3" buildToolsVersion "28.0.2"
defaultConfig { defaultConfig {
applicationId "nodomain.freeyourgadget.gadgetbridge" applicationId "nodomain.freeyourgadget.gadgetbridge"
@ -25,8 +25,8 @@ android {
targetSdkVersion 27 targetSdkVersion 27
// Note: always bump BOTH versionCode and versionName! // Note: always bump BOTH versionCode and versionName!
versionName "0.29.1" versionName "0.30.0"
versionCode 137 versionCode 138
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
buildTypes { buildTypes {
@ -143,7 +143,7 @@ task findbugs(type: FindBugs) {
effort = "default" effort = "default"
reportLevel = "medium" reportLevel = "medium"
excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-filter.xml") excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-filter.xml")
classes = files("${project.rootDir}/app/build/intermediates/classes") classes = files("${project.rootDir}/app/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes")
source = fileTree("src/main/java/") source = fileTree("src/main/java/")
classpath = files() classpath = files()
reports { reports {

View File

@ -183,7 +183,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
BaseActivitySummary item = getItemAdapter().getItem(checked.keyAt(i)); BaseActivitySummary item = getItemAdapter().getItem(checked.keyAt(i));
if (item != null) { if (item != null) {
ActivitySummary summary = (ActivitySummary) item; ActivitySummary summary = item;
String gpxTrack = summary.getGpxTrack(); String gpxTrack = summary.getGpxTrack();
if (gpxTrack != null) { if (gpxTrack != null) {
@ -238,7 +238,7 @@ public class ActivitySummariesActivity extends AbstractListActivity<BaseActivity
Calendar date = Calendar.getInstance(); Calendar date = Calendar.getInstance();
date.set(year, monthOfYear, dayOfMonth); date.set(year, monthOfYear, dayOfMonth);
Long timestamp = date.getTimeInMillis() - 1000; long timestamp = date.getTimeInMillis() - 1000;
SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
editor.remove(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis"); //FIXME: key reconstruction is BAD editor.remove(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis"); //FIXME: key reconstruction is BAD
editor.putLong(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis", timestamp); editor.putLong(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis", timestamp);

View File

@ -88,7 +88,7 @@ public abstract class AbstractAppManagerFragment extends Fragment {
protected void refreshList() { protected void refreshList() {
appList.clear(); appList.clear();
ArrayList uuids = AppManagerActivity.getUuidsFromFile(getSortFilename()); ArrayList<UUID> uuids = AppManagerActivity.getUuidsFromFile(getSortFilename());
List<GBDeviceApp> systemApps = getSystemAppsInCategory(); List<GBDeviceApp> systemApps = getSystemAppsInCategory();
boolean needsRewrite = false; boolean needsRewrite = false;
for (GBDeviceApp systemApp : systemApps) { for (GBDeviceApp systemApp : systemApps) {
@ -106,11 +106,11 @@ public abstract class AbstractAppManagerFragment extends Fragment {
private void refreshListFromPebble(Intent intent) { private void refreshListFromPebble(Intent intent) {
appList.clear(); appList.clear();
int appCount = intent.getIntExtra("app_count", 0); int appCount = intent.getIntExtra("app_count", 0);
for (Integer i = 0; i < appCount; i++) { for (int i = 0; i < appCount; i++) {
String appName = intent.getStringExtra("app_name" + i.toString()); String appName = intent.getStringExtra("app_name" + i);
String appCreator = intent.getStringExtra("app_creator" + i.toString()); String appCreator = intent.getStringExtra("app_creator" + i);
UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i.toString())); UUID uuid = UUID.fromString(intent.getStringExtra("app_uuid" + i));
GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i.toString(), 0)]; GBDeviceApp.Type appType = GBDeviceApp.Type.values()[intent.getIntExtra("app_type" + i, 0)];
GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType); GBDeviceApp app = new GBDeviceApp(uuid, appName, appCreator, "", appType);
app.setOnDevice(true); app.setOnDevice(true);

View File

@ -230,7 +230,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
} }
protected void showDateBar(boolean show) { protected void showDateBar(boolean show) {
getChartsHost().getDateBar().setVisibility(show ? View.VISIBLE : View.INVISIBLE); getChartsHost().getDateBar().setVisibility(show ? View.VISIBLE : View.GONE);
} }
@Override @Override

View File

@ -23,6 +23,7 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.charts.PieChart;
@ -32,6 +33,7 @@ import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.PieEntry;
@ -56,12 +58,14 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
public abstract class AbstractWeekChartFragment extends AbstractChartFragment { public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class); protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
protected final int TOTAL_DAYS = 7;
private Locale mLocale; private Locale mLocale;
private int mTargetValue = 0; private int mTargetValue = 0;
private PieChart mTodayPieChart; private PieChart mTodayPieChart;
private BarChart mWeekChart; private BarChart mWeekChart;
private TextView mBalanceView;
private int mOffsetHours = getOffsetHours(); private int mOffsetHours = getOffsetHours();
@ -71,7 +75,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
day.setTime(chartsHost.getEndDate()); day.setTime(chartsHost.getEndDate());
//NB: we could have omitted the day, but this way we can move things to the past easily //NB: we could have omitted the day, but this way we can move things to the past easily
DayData dayData = refreshDayPie(db, day, device); DayData dayData = refreshDayPie(db, day, device);
DefaultChartsData weekBeforeData = refreshWeekBeforeData(db, mWeekChart, day, device); WeekChartsData weekBeforeData = refreshWeekBeforeData(db, mWeekChart, day, device);
return new MyChartsData(dayData, weekBeforeData); return new MyChartsData(dayData, weekBeforeData);
} }
@ -87,23 +91,28 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mWeekChart.setData(mcd.getWeekBeforeData().getData()); mWeekChart.setData(mcd.getWeekBeforeData().getData());
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter()); mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
mBalanceView.setText(mcd.getWeekBeforeData().getBalanceMessage());
} }
@Override @Override
protected void renderCharts() { protected void renderCharts() {
mWeekChart.invalidate(); mWeekChart.invalidate();
mTodayPieChart.invalidate(); mTodayPieChart.invalidate();
// mBalanceView.setText(getBalanceMessage(balance));
} }
private DefaultChartsData<BarData> refreshWeekBeforeData(DBHandler db, BarChart barChart, Calendar day, GBDevice device) { private WeekChartsData<BarData> refreshWeekBeforeData(DBHandler db, BarChart barChart, Calendar day, GBDevice device) {
day = (Calendar) day.clone(); // do not modify the caller's argument day = (Calendar) day.clone(); // do not modify the caller's argument
day.add(Calendar.DATE, -7); day.add(Calendar.DATE, -TOTAL_DAYS);
List<BarEntry> entries = new ArrayList<>(); List<BarEntry> entries = new ArrayList<>();
ArrayList<String> labels = new ArrayList<String>(); ArrayList<String> labels = new ArrayList<String>();
for (int counter = 0; counter < 7; counter++) { long balance = 0;
for (int counter = 0; counter < TOTAL_DAYS; counter++) {
ActivityAmounts amounts = getActivityAmountsForDay(db, day, device); ActivityAmounts amounts = getActivityAmountsForDay(db, day, device);
balance += calculateBalance(amounts);
entries.add(new BarEntry(counter, getTotalsForActivityAmounts(amounts))); entries.add(new BarEntry(counter, getTotalsForActivityAmounts(amounts)));
labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale)); labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale));
day.add(Calendar.DATE, 1); day.add(Calendar.DATE, 1);
@ -121,7 +130,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
barChart.getAxisLeft().removeAllLimitLines(); barChart.getAxisLeft().removeAllLimitLines();
barChart.getAxisLeft().addLimitLine(target); barChart.getAxisLeft().addLimitLine(target);
return new DefaultChartsData(barData, new PreformattedXIndexLabelFormatter(labels)); return new WeekChartsData(barData, new PreformattedXIndexLabelFormatter(labels), getBalanceMessage(balance, mTargetValue));
} }
private DayData refreshDayPie(DBHandler db, Calendar day, GBDevice device) { private DayData refreshDayPie(DBHandler db, Calendar day, GBDevice device) {
@ -162,7 +171,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
set.setValueFormatter(getPieValueFormatter()); set.setValueFormatter(getPieValueFormatter());
} }
return new DayData(data, formatPieValue((int) totalValue)); return new DayData(data, formatPieValue((long) totalValue));
} }
@Override @Override
@ -177,8 +186,9 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
mTargetValue = goal; mTargetValue = goal;
} }
mTodayPieChart = (PieChart) rootView.findViewById(R.id.todaystepschart); mTodayPieChart = rootView.findViewById(R.id.todaystepschart);
mWeekChart = (BarChart) rootView.findViewById(R.id.weekstepschart); mWeekChart = rootView.findViewById(R.id.weekstepschart);
mBalanceView = rootView.findViewById(R.id.balance);
setupWeekChart(); setupWeekChart();
setupTodayPieChart(); setupTodayPieChart();
@ -265,10 +275,10 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
} }
private static class MyChartsData extends ChartsData { private static class MyChartsData extends ChartsData {
private final DefaultChartsData<BarData> weekBeforeData; private final WeekChartsData<BarData> weekBeforeData;
private final DayData dayData; private final DayData dayData;
MyChartsData(DayData dayData, DefaultChartsData<BarData> weekBeforeData) { MyChartsData(DayData dayData, WeekChartsData<BarData> weekBeforeData) {
this.dayData = dayData; this.dayData = dayData;
this.weekBeforeData = weekBeforeData; this.weekBeforeData = weekBeforeData;
} }
@ -277,7 +287,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
return dayData; return dayData;
} }
DefaultChartsData<BarData> getWeekBeforeData() { WeekChartsData<BarData> getWeekBeforeData() {
return weekBeforeData; return weekBeforeData;
} }
} }
@ -311,7 +321,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
abstract float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts); abstract float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts);
abstract String formatPieValue(int value); abstract String formatPieValue(long value);
abstract String[] getPieLabels(); abstract String[] getPieLabels();
@ -324,4 +334,21 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
abstract int[] getColors(); abstract int[] getColors();
abstract String getPieDescription(int targetValue); abstract String getPieDescription(int targetValue);
protected abstract long calculateBalance(ActivityAmounts amounts);
protected abstract String getBalanceMessage(long balance, int targetValue);
private class WeekChartsData<T extends ChartData<?>> extends DefaultChartsData<T> {
private final String balanceMessage;
public WeekChartsData(T data, PreformattedXIndexLabelFormatter xIndexLabelFormatter, String balanceMessage) {
super(data, xIndexLabelFormatter);
this.balanceMessage = balanceMessage;
}
public String getBalanceMessage() {
return balanceMessage;
}
}
} }

View File

@ -121,7 +121,7 @@ class ActivityAnalysis {
for (ActivitySample sample : samples) { for (ActivitySample sample : samples) {
int steps = sample.getSteps(); int steps = sample.getSteps();
if (steps > 0) { if (steps > 0) {
totalSteps += sample.getSteps(); totalSteps += steps;
} }
} }
return totalSteps; return totalSteps;

View File

@ -29,6 +29,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.charts.BarLineChartBase;
@ -60,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.Measurement; import nodomain.freeyourgadget.gadgetbridge.model.Measurement;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -78,13 +80,14 @@ public class LiveActivityFragment extends AbstractChartFragment {
private BarLineChartBase mStepsPerMinuteHistoryChart; private BarLineChartBase mStepsPerMinuteHistoryChart;
private CustomBarChart mStepsPerMinuteCurrentChart; private CustomBarChart mStepsPerMinuteCurrentChart;
private CustomBarChart mTotalStepsChart; private CustomBarChart mTotalStepsChart;
private TextView mMaxHeartRateView;
private final Steps mSteps = new Steps(); private final Steps mSteps = new Steps();
private ScheduledExecutorService pulseScheduler; private ScheduledExecutorService pulseScheduler;
private int maxStepsResetCounter; private int maxStepsResetCounter;
private List<Measurement> heartRateValues;
private LineDataSet mHeartRateSet; private LineDataSet mHeartRateSet;
private int mHeartRate; private int mHeartRate;
private int mMaxHeartRate = 0;
private TimestampTranslation tsTranslation; private TimestampTranslation tsTranslation;
private class Steps { private class Steps {
@ -172,7 +175,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
setCurrentHeartRate(heartRate, timestamp); setCurrentHeartRate(heartRate, timestamp);
} }
int steps = sample.getSteps(); int steps = sample.getSteps();
if (steps != ActivitySample.NOT_MEASURED) { if (steps > 0) {
addEntries(steps, timestamp); addEntries(steps, timestamp);
} }
} }
@ -189,6 +192,10 @@ public class LiveActivityFragment extends AbstractChartFragment {
private void setCurrentHeartRate(int heartRate, int timestamp) { private void setCurrentHeartRate(int heartRate, int timestamp) {
addHistoryDataSet(true); addHistoryDataSet(true);
mHeartRate = heartRate; mHeartRate = heartRate;
if (mMaxHeartRate < mHeartRate) {
mMaxHeartRate = mHeartRate;
}
mMaxHeartRateView.setText(getContext().getString(R.string.live_activity_max_heart_rate, heartRate, mMaxHeartRate));
} }
private int getCurrentHeartRate() { private int getCurrentHeartRate() {
@ -233,10 +240,9 @@ public class LiveActivityFragment extends AbstractChartFragment {
} }
mHistorySet.addEntry(new Entry(timestamp, stepsPerMinute)); mHistorySet.addEntry(new Entry(timestamp, stepsPerMinute));
int hr = getCurrentHeartRate(); int hr = getCurrentHeartRate();
if (hr < 0) { if (hr > HeartRateUtils.getInstance().getMinHeartRate()) {
hr = 0; mHeartRateSet.addEntry(new Entry(timestamp, hr));
} }
mHeartRateSet.addEntry(new Entry(timestamp, hr));
} }
private boolean addHistoryDataSet(boolean force) { private boolean addHistoryDataSet(boolean force) {
@ -259,21 +265,25 @@ public class LiveActivityFragment extends AbstractChartFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
IntentFilter filterLocal = new IntentFilter(); IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES); filterLocal.addAction(DeviceService.ACTION_REALTIME_SAMPLES);
heartRateValues = new ArrayList<>();
tsTranslation = new TimestampTranslation(); tsTranslation = new TimestampTranslation();
View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false); View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false);
mStepsPerMinuteCurrentChart = (CustomBarChart) rootView.findViewById(R.id.livechart_steps_per_minute_current); mStepsPerMinuteCurrentChart = rootView.findViewById(R.id.livechart_steps_per_minute_current);
mTotalStepsChart = (CustomBarChart) rootView.findViewById(R.id.livechart_steps_total); mTotalStepsChart = rootView.findViewById(R.id.livechart_steps_total);
mStepsPerMinuteHistoryChart = (BarLineChartBase) rootView.findViewById(R.id.livechart_steps_per_minute_history); mStepsPerMinuteHistoryChart = rootView.findViewById(R.id.livechart_steps_per_minute_history);
totalStepsEntry = new BarEntry(1, 0); totalStepsEntry = new BarEntry(1, 0);
stepsPerMinuteEntry = new BarEntry(1, 0); stepsPerMinuteEntry = new BarEntry(1, 0);
mStepsPerMinuteData = setupCurrentChart(mStepsPerMinuteCurrentChart, stepsPerMinuteEntry, getString(R.string.live_activity_current_steps_per_minute)); mStepsPerMinuteData = setupCurrentChart(mStepsPerMinuteCurrentChart, stepsPerMinuteEntry, getString(R.string.live_activity_current_steps_per_minute));
mStepsPerMinuteData.setDrawValues(true);
mStepsPerMinuteData.setValueTextColor(DESCRIPTION_COLOR);
mTotalStepsData = setupTotalStepsChart(mTotalStepsChart, totalStepsEntry, getString(R.string.live_activity_total_steps)); mTotalStepsData = setupTotalStepsChart(mTotalStepsChart, totalStepsEntry, getString(R.string.live_activity_total_steps));
mTotalStepsData.setDrawValues(true);
mTotalStepsData.setValueTextColor(DESCRIPTION_COLOR);
setupHistoryChart(mStepsPerMinuteHistoryChart); setupHistoryChart(mStepsPerMinuteHistoryChart);
mMaxHeartRateView = rootView.findViewById(R.id.livechart_max_heart_rate);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filterLocal); LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filterLocal);
@ -385,7 +395,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
} }
private BarDataSet setupCurrentChart(CustomBarChart chart, BarEntry entry, String title) { private BarDataSet setupCurrentChart(CustomBarChart chart, BarEntry entry, String title) {
mStepsPerMinuteCurrentChart.getAxisLeft().setAxisMaxValue(MAX_STEPS_PER_MINUTE); mStepsPerMinuteCurrentChart.getAxisLeft().setAxisMaximum(MAX_STEPS_PER_MINUTE);
return setupCommonChart(chart, entry, title); return setupCommonChart(chart, entry, title);
} }
@ -408,9 +418,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
List<BarEntry> entries = new ArrayList<>(); List<BarEntry> entries = new ArrayList<>();
List<Integer> colors = new ArrayList<>(); List<Integer> colors = new ArrayList<>();
entries.add(new BarEntry(0, 0));
entries.add(entry); entries.add(entry);
entries.add(new BarEntry(2, 0));
colors.add(akActivity.color); colors.add(akActivity.color);
colors.add(akActivity.color); colors.add(akActivity.color);
colors.add(akActivity.color); colors.add(akActivity.color);
@ -432,7 +440,9 @@ public class LiveActivityFragment extends AbstractChartFragment {
} }
private BarDataSet setupTotalStepsChart(CustomBarChart chart, BarEntry entry, String label) { private BarDataSet setupTotalStepsChart(CustomBarChart chart, BarEntry entry, String label) {
mTotalStepsChart.getAxisLeft().setAxisMaximum(5000); // TODO: use daily goal - already reached steps mTotalStepsChart.getAxisLeft().addLimitLine(new LimitLine(GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal), "ss")); // TODO: use daily goal - already reached steps
mTotalStepsChart.getAxisLeft().setAxisMinimum(0);
mTotalStepsChart.setAutoScaleMinMaxEnabled(true);
return setupCommonChart(chart, entry, label); // at the moment, these look the same return setupCommonChart(chart, entry, label); // at the moment, these look the same
} }
@ -455,6 +465,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
x.setDrawGridLines(false); x.setDrawGridLines(false);
x.setEnabled(true); x.setEnabled(true);
x.setTextColor(CHART_TEXT_COLOR); x.setTextColor(CHART_TEXT_COLOR);
x.setValueFormatter(new SampleXLabelFormatter(tsTranslation));
x.setDrawLimitLinesBehindData(true); x.setDrawLimitLinesBehindData(true);
YAxis y = chart.getAxisLeft(); YAxis y = chart.getAxisLeft();
@ -470,8 +481,6 @@ public class LiveActivityFragment extends AbstractChartFragment {
yAxisRight.setDrawLabels(true); yAxisRight.setDrawLabels(true);
yAxisRight.setDrawTopYLabelEntry(false); yAxisRight.setDrawTopYLabelEntry(false);
yAxisRight.setTextColor(CHART_TEXT_COLOR); yAxisRight.setTextColor(CHART_TEXT_COLOR);
yAxisRight.setAxisMaximum(HeartRateUtils.getInstance().getMaxHeartRate());
yAxisRight.setAxisMinimum(HeartRateUtils.getInstance().getMinHeartRate());
mHistorySet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_steps_history)); mHistorySet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_steps_history));
mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT); mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT);

View File

@ -58,6 +58,31 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
return -12; return -12;
} }
@Override
protected long calculateBalance(ActivityAmounts activityAmounts) {
long balance = 0;
for (ActivityAmount amount : activityAmounts.getAmounts()) {
if (amount.getActivityKind() == ActivityKind.TYPE_DEEP_SLEEP || amount.getActivityKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
balance += amount.getTotalSeconds();
}
}
return (int) (balance / 60);
}
@Override
protected String getBalanceMessage(long balance, int targetValue) {
if (balance > 0) {
final long totalBalance = balance - (targetValue * TOTAL_DAYS);
if (totalBalance > 0)
return getString(R.string.overslept, getHM(totalBalance));
else
return getString(R.string.lack_of_sleep, getHM(Math.abs(totalBalance)));
} else
return getString(R.string.no_data);
}
@Override @Override
float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts) { float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts) {
long totalSecondsDeepSleep = 0; long totalSecondsDeepSleep = 0;
@ -69,12 +94,14 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
totalSecondsLightSleep += amount.getTotalSeconds(); totalSecondsLightSleep += amount.getTotalSeconds();
} }
} }
return new float[]{(int) (totalSecondsDeepSleep / 60), (int) (totalSecondsLightSleep / 60)}; int totalMinutesDeepSleep = (int) (totalSecondsDeepSleep / 60);
int totalMinutesLightSleep = (int) (totalSecondsLightSleep / 60);
return new float[]{totalMinutesDeepSleep, totalMinutesLightSleep};
} }
@Override @Override
protected String formatPieValue(int value) { protected String formatPieValue(long value) {
return DateTimeUtils.formatDurationHoursMinutes((long) value, TimeUnit.MINUTES); return DateTimeUtils.formatDurationHoursMinutes(value, TimeUnit.MINUTES);
} }
@Override @Override
@ -87,7 +114,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
return new IValueFormatter() { return new IValueFormatter() {
@Override @Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return formatPieValue((int) value); return formatPieValue((long) value);
} }
}; };
} }
@ -136,4 +163,8 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
chart.getLegend().setWordWrapEnabled(true); chart.getLegend().setWordWrapEnabled(true);
chart.getLegend().setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); chart.getLegend().setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
} }
private String getHM(long value) {
return DateTimeUtils.formatDurationHoursMinutes(value, TimeUnit.MINUTES);
}
} }

View File

@ -40,7 +40,7 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment {
@Override @Override
int getGoal() { int getGoal() {
return GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, 10000); return GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
} }
@Override @Override
@ -50,16 +50,24 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment {
@Override @Override
float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts) { float[] getTotalsForActivityAmounts(ActivityAmounts activityAmounts) {
int totalSteps = 0; long totalSteps = 0;
for (ActivityAmount amount : activityAmounts.getAmounts()) { for (ActivityAmount amount : activityAmounts.getAmounts()) {
totalSteps += amount.getTotalSteps(); totalSteps += amount.getTotalSteps();
amount.getTotalSteps();
} }
return new float[]{totalSteps}; return new float[]{totalSteps};
} }
@Override @Override
protected String formatPieValue(int value) { protected long calculateBalance(ActivityAmounts activityAmounts) {
long balance = 0;
for (ActivityAmount amount : activityAmounts.getAmounts()) {
balance += amount.getTotalSteps();
}
return balance;
}
@Override
protected String formatPieValue(long value) {
return String.valueOf(value); return String.valueOf(value);
} }
@ -93,4 +101,16 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment {
// no legend here, it is all about the steps here // no legend here, it is all about the steps here
chart.getLegend().setEnabled(false); chart.getLegend().setEnabled(false);
} }
@Override
protected String getBalanceMessage(long balance, int targetValue) {
if (balance > 0) {
final long totalBalance = balance - (targetValue * TOTAL_DAYS);
if (totalBalance > 0)
return getString(R.string.overstep, Math.abs(totalBalance));
else
return getString(R.string.lack_of_step, Math.abs(totalBalance));
} else
return getString(R.string.no_data);
}
} }

View File

@ -146,7 +146,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
} }
} else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) { } else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) {
holder.batteryStatusBox.setVisibility(View.VISIBLE); holder.batteryStatusBox.setVisibility(View.VISIBLE);
holder.batteryStatusLabel.setText(String.format(Locale.getDefault(), "%.1fV", batteryVoltage)); holder.batteryStatusLabel.setText(String.format(Locale.getDefault(), "%.2f", batteryVoltage));
holder.batteryIcon.setImageLevel(200); holder.batteryIcon.setImageLevel(200);
} }
@ -378,16 +378,23 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
ColorPickerDialog.Builder builder = ColorPickerDialog.newBuilder(); ColorPickerDialog.Builder builder = ColorPickerDialog.newBuilder();
builder.setDialogTitle(R.string.preferences_led_color); builder.setDialogTitle(R.string.preferences_led_color);
int[] presets = coordinator.getColorPresets();
builder.setColor((int) device.getExtraInfo("led_color")); builder.setColor((int) device.getExtraInfo("led_color"));
builder.setShowAlphaSlider(false);
builder.setShowColorShades(false);
if (coordinator.supportsRgbLedColor()) { if (coordinator.supportsRgbLedColor()) {
builder.setAllowCustom(true); builder.setAllowCustom(true);
builder.setShowAlphaSlider(false); if (presets.length == 0) {
builder.setAllowPresets(true); builder.setDialogType(ColorPickerDialog.TYPE_CUSTOM);
}
} else { } else {
builder.setAllowCustom(false); builder.setAllowCustom(false);
}
if (presets.length > 0) {
builder.setAllowPresets(true); builder.setAllowPresets(true);
builder.setShowColorShades(false); builder.setPresets(presets);
builder.setPresets(coordinator.getColorPresets());
} }
ColorPickerDialog dialog = builder.create(); ColorPickerDialog dialog = builder.create();

View File

@ -255,7 +255,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
} }
public static byte getAllDayHR(String address) { public static byte getAllDayHR(String address) {
Boolean value = (prefs.getBoolean(HPlusConstants.PREF_HPLUS_ALLDAYHR, true)); boolean value = (prefs.getBoolean(HPlusConstants.PREF_HPLUS_ALLDAYHR, true));
if(value){ if(value){
return HPlusConstants.ARG_HEARTRATE_ALLDAY_ON; return HPlusConstants.ARG_HEARTRATE_ALLDAY_ON;

View File

@ -49,9 +49,4 @@ public class Roidmi1Coordinator extends RoidmiCoordinator {
public DeviceType getDeviceType() { public DeviceType getDeviceType() {
return DeviceType.ROIDMI; return DeviceType.ROIDMI;
} }
@Override
public int[] getColorPresets() {
return RoidmiConst.COLOR_PRESETS;
}
} }

View File

@ -36,8 +36,7 @@ public class Roidmi3Coordinator extends RoidmiCoordinator {
String name = device.getName(); String name = device.getName();
if (name != null && name.contains("Roidmi Music Blue C")) { if (name != null && name.contains("Roidmi Music Blue C")) {
LOG.warn("Found a Roidmi 3, but support is disabled."); return DeviceType.ROIDMI3;
return DeviceType.UNKNOWN; // TODO Roidmi 3 is not working atm
} }
} catch (Exception ex) { } catch (Exception ex) {
LOG.error("unable to check device support", ex); LOG.error("unable to check device support", ex);

View File

@ -132,4 +132,9 @@ public abstract class RoidmiCoordinator extends AbstractDeviceCoordinator {
public boolean supportsLedColor() { public boolean supportsLedColor() {
return true; return true;
} }
@Override
public int[] getColorPresets() {
return RoidmiConst.COLOR_PRESETS;
}
} }

View File

@ -15,124 +15,124 @@
You should have received a copy of the GNU Affero General Public License 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/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.xwatch; package nodomain.freeyourgadget.gadgetbridge.devices.xwatch;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class XWatchCoordinator extends AbstractDeviceCoordinator { public class XWatchCoordinator extends AbstractDeviceCoordinator {
@NonNull @NonNull
@Override @Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) { public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName(); String name = candidate.getDevice().getName();
if (name != null && name.startsWith("XWatch")) { if (name != null && name.startsWith("XWatch")) {
return DeviceType.XWATCH; return DeviceType.XWATCH;
} }
return DeviceType.UNKNOWN; return DeviceType.UNKNOWN;
} }
@Override @Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
} }
@Override @Override
public DeviceType getDeviceType() { public DeviceType getDeviceType() {
return DeviceType.XWATCH; return DeviceType.XWATCH;
} }
@Nullable @Nullable
@Override @Override
public Class<? extends Activity> getPairingActivity() { public Class<? extends Activity> getPairingActivity() {
return null; return null;
} }
@Override @Override
public boolean supportsActivityDataFetching() { public boolean supportsActivityDataFetching() {
return true; return true;
} }
@Override @Override
public boolean supportsActivityTracking() { public boolean supportsActivityTracking() {
return true; return true;
} }
@Override @Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) { public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return new XWatchSampleProvider(device, session); return new XWatchSampleProvider(device, session);
} }
@Override @Override
public InstallHandler findInstallHandler(Uri uri, Context context) { public InstallHandler findInstallHandler(Uri uri, Context context) {
return null; return null;
} }
@Override @Override
public boolean supportsScreenshots() { public boolean supportsScreenshots() {
return false; return false;
} }
@Override @Override
public boolean supportsAlarmConfiguration() { public boolean supportsAlarmConfiguration() {
return true; return true;
} }
@Override @Override
public boolean supportsSmartWakeup(GBDevice device) { public boolean supportsSmartWakeup(GBDevice device) {
return false; return false;
} }
@Override @Override
public boolean supportsHeartRateMeasurement(GBDevice device) { public boolean supportsHeartRateMeasurement(GBDevice device) {
return false; return false;
} }
@Override @Override
public String getManufacturer() { public String getManufacturer() {
return "Generic"; return "Generic";
} }
@Override @Override
public boolean supportsAppsManagement() { public boolean supportsAppsManagement() {
return false; return false;
} }
@Override @Override
public Class<? extends Activity> getAppsManagementActivity() { public Class<? extends Activity> getAppsManagementActivity() {
return null; return null;
} }
@Override @Override
public boolean supportsCalendarEvents() { public boolean supportsCalendarEvents() {
return false; return false;
} }
@Override @Override
public boolean supportsRealtimeData() { public boolean supportsRealtimeData() {
return false; return false;
} }
@Override @Override
public boolean supportsWeather() { public boolean supportsWeather() {
return false; return false;
} }
@Override @Override
public boolean supportsFindDevice() { public boolean supportsFindDevice() {
return true; return true;
} }
} }

View File

@ -14,37 +14,37 @@
You should have received a copy of the GNU Affero General Public License 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/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.xwatch; package nodomain.freeyourgadget.gadgetbridge.devices.xwatch;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
public class XWatchService { public class XWatchService {
public static final UUID UUID_NOTIFY = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb"); public static final UUID UUID_NOTIFY = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb"); public static final UUID UUID_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_WRITE = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb"); public static final UUID UUID_WRITE = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb");
public static final byte COMMAND_CONNECTED = 0x01; public static final byte COMMAND_CONNECTED = 0x01;
public static final byte COMMAND_ACTION_BUTTON = 0x4c; public static final byte COMMAND_ACTION_BUTTON = 0x4c;
public static final byte COMMAND_ACTIVITY_DATA = 0x43; public static final byte COMMAND_ACTIVITY_DATA = 0x43;
public static final byte COMMAND_ACTIVITY_TOTALS = 0x46; public static final byte COMMAND_ACTIVITY_TOTALS = 0x46;
private static final Map<UUID, String> XWATCH_DEBUG; private static final Map<UUID, String> XWATCH_DEBUG;
static { static {
XWATCH_DEBUG = new HashMap<>(); XWATCH_DEBUG = new HashMap<>();
XWATCH_DEBUG.put(UUID_NOTIFY, "Read data"); XWATCH_DEBUG.put(UUID_NOTIFY, "Read data");
XWATCH_DEBUG.put(UUID_WRITE, "Write data"); XWATCH_DEBUG.put(UUID_WRITE, "Write data");
XWATCH_DEBUG.put(UUID_SERVICE, "Get service"); XWATCH_DEBUG.put(UUID_SERVICE, "Get service");
} }
public static String lookup(UUID uuid, String fallback) { public static String lookup(UUID uuid, String fallback) {
String name = XWATCH_DEBUG.get(uuid); String name = XWATCH_DEBUG.get(uuid);
if (name == null) { if (name == null) {
name = fallback; name = fallback;
} }
return name; return name;
} }
} }

View File

@ -60,8 +60,8 @@ public class GPXExporter implements ActivityTrackExporter {
public void performExport(ActivityTrack track, File targetFile) throws IOException, GPXTrackEmptyException { public void performExport(ActivityTrack track, File targetFile) throws IOException, GPXTrackEmptyException {
String encoding = StandardCharsets.UTF_8.name(); String encoding = StandardCharsets.UTF_8.name();
XmlSerializer ser = Xml.newSerializer(); XmlSerializer ser = Xml.newSerializer();
try { try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
ser.setOutput(new FileOutputStream(targetFile), encoding); ser.setOutput(outputStream, encoding);
ser.startDocument(encoding, Boolean.TRUE); ser.startDocument(encoding, Boolean.TRUE);
ser.setPrefix("xsi", NS_XSI_URI); ser.setPrefix("xsi", NS_XSI_URI);
ser.setPrefix(NS_TRACKPOINT_EXTENSION, NS_TRACKPOINT_EXTENSION_URI); ser.setPrefix(NS_TRACKPOINT_EXTENSION, NS_TRACKPOINT_EXTENSION_URI);
@ -77,7 +77,6 @@ public class GPXExporter implements ActivityTrackExporter {
ser.endTag(NS_DEFAULT, "gpx"); ser.endTag(NS_DEFAULT, "gpx");
ser.endDocument(); ser.endDocument();
} finally {
ser.flush(); ser.flush();
} }
} }

View File

@ -272,9 +272,9 @@ public class NotificationListener extends NotificationListenerService {
// Ignore too frequent notifications, according to user preference // Ignore too frequent notifications, according to user preference
long min_timeout = prefs.getInt("notifications_timeout", 0) * 1000; long min_timeout = prefs.getInt("notifications_timeout", 0) * 1000;
Long cur_time = System.currentTimeMillis(); long cur_time = System.currentTimeMillis();
if (notificationTimes.containsKey(source)) { if (notificationTimes.containsKey(source)) {
Long last_time = notificationTimes.get(source); long last_time = notificationTimes.get(source);
if (cur_time - last_time < min_timeout) { if (cur_time - last_time < min_timeout) {
LOG.info("Ignoring frequent notification, last one was " + (cur_time - last_time) + "ms ago"); LOG.info("Ignoring frequent notification, last one was " + (cur_time - last_time) + "ms ago");
return; return;

View File

@ -274,6 +274,7 @@ public class GBDevice implements Parcelable {
setFirmwareVersion(null); setFirmwareVersion(null);
setFirmwareVersion2(null); setFirmwareVersion2(null);
setRssi(RSSI_UNKNOWN); setRssi(RSSI_UNKNOWN);
resetExtraInfos();
if (mBusyTask != null) { if (mBusyTask != null) {
unsetBusyTask(); unsetBusyTask();
} }
@ -403,6 +404,13 @@ public class GBDevice implements Parcelable {
mExtraInfos.put(key, info); mExtraInfos.put(key, info);
} }
/**
* Deletes all the extra infos
*/
public void resetExtraInfos() {
mExtraInfos = null;
}
/** /**
* Ranges from 0-100 (percent), or -1 if unknown * Ranges from 0-100 (percent), or -1 if unknown
* *

View File

@ -71,9 +71,9 @@ public class CalendarEvents {
private boolean fetchSystemEvents(Context mContext) { private boolean fetchSystemEvents(Context mContext) {
Calendar cal = GregorianCalendar.getInstance(); Calendar cal = GregorianCalendar.getInstance();
Long dtStart = cal.getTimeInMillis(); long dtStart = cal.getTimeInMillis();
cal.add(Calendar.DATE, lookahead_days); cal.add(Calendar.DATE, lookahead_days);
Long dtEnd = cal.getTimeInMillis(); long dtEnd = cal.getTimeInMillis();
Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon(); Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(eventsUriBuilder, dtStart); ContentUris.appendId(eventsUriBuilder, dtStart);

View File

@ -216,7 +216,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
protected void handleGBDeviceEvent(GBDeviceEventLEDColor colorEvent) { protected void handleGBDeviceEvent(GBDeviceEventLEDColor colorEvent) {
Context context = getContext(); Context context = getContext();
LOG.info("Got event for LED Color"); LOG.info("Got event for LED Color: #" + Integer.toHexString(colorEvent.color).toUpperCase());
if (gbDevice == null) { if (gbDevice == null) {
return; return;
} }
@ -241,11 +241,11 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
Intent appInfoIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST); Intent appInfoIntent = new Intent(AbstractAppManagerFragment.ACTION_REFRESH_APPLIST);
int appCount = appInfoEvent.apps.length; int appCount = appInfoEvent.apps.length;
appInfoIntent.putExtra("app_count", appCount); appInfoIntent.putExtra("app_count", appCount);
for (Integer i = 0; i < appCount; i++) { for (int i = 0; i < appCount; i++) {
appInfoIntent.putExtra("app_name" + i.toString(), appInfoEvent.apps[i].getName()); appInfoIntent.putExtra("app_name" + i, appInfoEvent.apps[i].getName());
appInfoIntent.putExtra("app_creator" + i.toString(), appInfoEvent.apps[i].getCreator()); appInfoIntent.putExtra("app_creator" + i, appInfoEvent.apps[i].getCreator());
appInfoIntent.putExtra("app_uuid" + i.toString(), appInfoEvent.apps[i].getUUID().toString()); appInfoIntent.putExtra("app_uuid" + i, appInfoEvent.apps[i].getUUID().toString());
appInfoIntent.putExtra("app_type" + i.toString(), appInfoEvent.apps[i].getType().ordinal()); appInfoIntent.putExtra("app_type" + i, appInfoEvent.apps[i].getType().ordinal());
} }
LocalBroadcastManager.getInstance(context).sendBroadcast(appInfoIntent); LocalBroadcastManager.getInstance(context).sendBroadcast(appInfoIntent);
} }

View File

@ -339,6 +339,19 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mGBDevice.sendDeviceUpdateIntent(this); mGBDevice.sendDeviceUpdateIntent(this);
} }
break; break;
default:
if (mDeviceSupport == null || mGBDevice == null) {
LOG.warn("device support:" + mDeviceSupport + ", device: " + mGBDevice + ", aborting");
} else {
handleAction(intent, action, prefs);
}
break;
}
return START_STICKY;
}
private void handleAction(Intent intent, String action, Prefs prefs) {
switch (action) {
case ACTION_REQUEST_DEVICEINFO: case ACTION_REQUEST_DEVICEINFO:
mGBDevice.sendDeviceUpdateIntent(this); mGBDevice.sendDeviceUpdateIntent(this);
break; break;
@ -529,7 +542,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
break; break;
} }
case ACTION_SET_HEARTRATE_MEASUREMENT_INTERVAL: { case ACTION_SET_HEARTRATE_MEASUREMENT_INTERVAL: {
Integer seconds = intent.getIntExtra(EXTRA_INTERVAL_SECONDS, 0); int seconds = intent.getIntExtra(EXTRA_INTERVAL_SECONDS, 0);
mDeviceSupport.onSetHeartRateMeasurementInterval(seconds); mDeviceSupport.onSetHeartRateMeasurementInterval(seconds);
break; break;
} }
@ -567,8 +580,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
} }
break; break;
} }
return START_STICKY;
} }
/** /**

View File

@ -108,6 +108,16 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
return builder; return builder;
} }
public TransactionBuilder createTransactionBuilder(String taskName) {
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
builder.setGattCallback(this);
return builder;
}
public void performImmediately(TransactionBuilder builder) throws IOException {
mSupport.performImmediately(builder);
}
protected Context getContext() { protected Context getContext() {
return mSupport.getContext(); return mSupport.getContext();
} }

View File

@ -26,6 +26,8 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -38,6 +40,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
@ -104,6 +107,11 @@ public final class BtLEQueue {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("About to run action: " + action); LOG.debug("About to run action: " + action);
} }
if (action instanceof GattListenerAction) {
// this special action overwrites the transaction gatt listener (if any), it must
// always be the last action in the transaction
internalGattCallback.setTransactionGattCallback(((GattListenerAction)action).getGattCallback());
}
if (action.run(mBluetoothGatt)) { if (action.run(mBluetoothGatt)) {
// check again, maybe due to some condition, action did not need to write, so we can't wait // check again, maybe due to some condition, action did not need to write, so we can't wait
boolean waitForResult = action.expectsResult(); boolean waitForResult = action.expectsResult();
@ -177,7 +185,11 @@ public final class BtLEQueue {
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress()); BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress());
synchronized (mGattMonitor) { synchronized (mGattMonitor) {
// connectGatt with true doesn't really work ;( too often connection problems // connectGatt with true doesn't really work ;( too often connection problems
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback); if (GBApplication.isRunningMarshmallowOrLater()) {
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback);
}
} }
boolean result = mBluetoothGatt != null; boolean result = mBluetoothGatt != null;
if (result) { if (result) {
@ -371,8 +383,16 @@ public final class BtLEQueue {
LOG.info("Using cached services, skipping discovery"); LOG.info("Using cached services, skipping discovery");
onServicesDiscovered(gatt, BluetoothGatt.GATT_SUCCESS); onServicesDiscovered(gatt, BluetoothGatt.GATT_SUCCESS);
} else { } else {
LOG.info("Attempting to start service discovery:" + LOG.info("Attempting to start service discovery");
gatt.discoverServices()); // discover services in the main thread (appears to fix Samsung connection problems)
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (mBluetoothGatt != null) {
mBluetoothGatt.discoverServices();
}
}
});
} }
break; break;
case BluetoothProfile.STATE_DISCONNECTED: case BluetoothProfile.STATE_DISCONNECTED:
@ -502,7 +522,9 @@ public final class BtLEQueue {
private void checkWaitingCharacteristic(BluetoothGattCharacteristic characteristic, int status) { private void checkWaitingCharacteristic(BluetoothGattCharacteristic characteristic, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) { if (status != BluetoothGatt.GATT_SUCCESS) {
LOG.debug("failed btle action, aborting transaction: " + characteristic.getUuid() + getStatusString(status)); if (characteristic != null) {
LOG.debug("failed btle action, aborting transaction: " + characteristic.getUuid() + getStatusString(status));
}
mAbortTransaction = true; mAbortTransaction = true;
} }
if (characteristic != null && BtLEQueue.this.mWaitCharacteristic != null && characteristic.getUuid().equals(BtLEQueue.this.mWaitCharacteristic.getUuid())) { if (characteristic != null && BtLEQueue.this.mWaitCharacteristic != null && characteristic.getUuid().equals(BtLEQueue.this.mWaitCharacteristic.getUuid())) {

View File

@ -0,0 +1,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.btle;
public interface GattListenerAction {
GattCallback getGattCallback();
}

View File

@ -68,6 +68,12 @@ public class TransactionBuilder {
return new NotifyAction(characteristic, enable); return new NotifyAction(characteristic, enable);
} }
/**
* Causes the queue to sleep for the specified time.
* Note that this is usually a bad idea, since it will not be able to process messages
* during that time. It is also likely to cause race conditions.
* @param millis the number of milliseconds to sleep
*/
public TransactionBuilder wait(int millis) { public TransactionBuilder wait(int millis) {
WaitAction action = new WaitAction(millis); WaitAction action = new WaitAction(millis);
return add(action); return add(action);
@ -110,4 +116,8 @@ public class TransactionBuilder {
public Transaction getTransaction() { public Transaction getTransaction() {
return mTransaction; return mTransaction;
} }
public String getTaskName() {
return mTransaction.getTaskName();
}
} }

View File

@ -0,0 +1,33 @@
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractGattCallback;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattListenerAction;
public abstract class AbstractGattListenerWriteAction extends WriteAction implements GattListenerAction {
private final BtLEQueue queue;
public AbstractGattListenerWriteAction(BtLEQueue queue, BluetoothGattCharacteristic characteristic, byte[] value) {
super(characteristic, value);
this.queue = queue;
Objects.requireNonNull(queue, "queue must not be null");
}
@Override
public GattCallback getGattCallback() {
return new AbstractGattCallback() {
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
return AbstractGattListenerWriteAction.this.onCharacteristicChanged(gatt, characteristic);
}
};
}
protected abstract boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
}

View File

@ -18,6 +18,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGatt;
/**
* An action that will cause the queue to sleep for the specified time.
* Note that this is usually a bad idea, since it will not be able to process messages
* during that time. It is also likely to cause race conditions.
*/
public class WaitAction extends PlainAction { public class WaitAction extends PlainAction {
private final int mMillis; private final int mMillis;

View File

@ -71,7 +71,7 @@ public class HeartRateProfile<T extends AbstractBTLEDeviceSupport> extends Abstr
format = BluetoothGattCharacteristic.FORMAT_UINT8; format = BluetoothGattCharacteristic.FORMAT_UINT8;
} }
final int heartRate = characteristic.getIntValue(format, 1); final int heartRate = characteristic.getIntValue(format, 1);
LOG.info("Heart rate: " + heartRate, Toast.LENGTH_LONG, GB.INFO); LOG.info("Heart rate: " + heartRate);
} }
return false; return false;
} }

View File

@ -179,6 +179,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
private boolean isMusicAppStarted = false; private boolean isMusicAppStarted = false;
private MusicSpec bufferMusicSpec = null; private MusicSpec bufferMusicSpec = null;
private MusicStateSpec bufferMusicStateSpec = null; private MusicStateSpec bufferMusicStateSpec = null;
private boolean heartRateNotifyEnabled;
public HuamiSupport() { public HuamiSupport() {
this(LOG); this(LOG);
@ -207,6 +208,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
@Override @Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) { protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
try { try {
heartRateNotifyEnabled = false;
boolean authenticate = needsAuth; boolean authenticate = needsAuth;
needsAuth = false; needsAuth = false;
byte authFlags = HuamiService.AUTH_BYTE; byte authFlags = HuamiService.AUTH_BYTE;
@ -377,7 +379,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
LOG.info("Attempting to set Fitness Goal..."); LOG.info("Attempting to set Fitness Goal...");
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS); BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS);
if (characteristic != null) { if (characteristic != null) {
int fitnessGoal = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, 10000); int fitnessGoal = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
byte[] bytes = ArrayUtils.addAll( byte[] bytes = ArrayUtils.addAll(
HuamiService.COMMAND_SET_FITNESS_GOAL_START, HuamiService.COMMAND_SET_FITNESS_GOAL_START,
BLETypeConversions.fromUint16(fitnessGoal)); BLETypeConversions.fromUint16(fitnessGoal));
@ -871,9 +873,12 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
} }
try { try {
TransactionBuilder builder = performInitialized("Enable realtime heart rate measurement"); TransactionBuilder builder = performInitialized("Enable realtime heart rate measurement");
BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); if (heartRateNotifyEnabled != enable) {
if (heartrateCharacteristic != null) { BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
builder.notify(heartrateCharacteristic, enable); if (heartrateCharacteristic != null) {
builder.notify(heartrateCharacteristic, enable);
heartRateNotifyEnabled = enable;
}
} }
if (enable) { if (enable) {
builder.write(characteristicHRControlPoint, stopHeartMeasurementManual); builder.write(characteristicHRControlPoint, stopHeartMeasurementManual);
@ -1343,7 +1348,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
MiBand2SampleProvider provider = new MiBand2SampleProvider(gbDevice, session); MiBand2SampleProvider provider = new MiBand2SampleProvider(gbDevice, session);
MiBandActivitySample sample = createActivitySample(device, user, ts, provider); MiBandActivitySample sample = createActivitySample(device, user, ts, provider);
sample.setHeartRate(getHeartrateBpm()); sample.setHeartRate(getHeartrateBpm());
sample.setSteps(getSteps()); // sample.setSteps(getSteps());
sample.setRawIntensity(ActivitySample.NOT_MEASURED); sample.setRawIntensity(ActivitySample.NOT_MEASURED);
sample.setRawKind(HuamiConst.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? sample.setRawKind(HuamiConst.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that?

View File

@ -45,6 +45,11 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
(byte) 0x8c, 0x36, 0x2e, (byte) 0x8c, (byte) 0x9c, 0x08, 0x54, (byte) 0xa6 (byte) 0x8c, 0x36, 0x2e, (byte) 0x8c, (byte) 0x9c, 0x08, 0x54, (byte) 0xa6
}; };
private static final byte[] GPS_HEADER5 = new byte[]{
(byte) 0xec, 0x51, 0x73, 0x22 , 0x60 ,0x02 ,0x14, (byte) 0xb7,
(byte) 0xb5, (byte) 0xea, 0x4b, 0x22 , 0x5d, 0x23, (byte) 0xe5, 0x4f
};
// this is the same as Cor // this is the same as Cor
private static final byte[] FW_HEADER = new byte[]{ private static final byte[] FW_HEADER = new byte[]{
0x00, (byte) 0x98, 0x00, 0x20, (byte) 0xA5, 0x04, 0x00, 0x20, (byte) 0xAD, 0x04, 0x00, 0x20, (byte) 0xC5, 0x04, 0x00, 0x20 0x00, (byte) 0x98, 0x00, 0x20, (byte) 0xA5, 0x04, 0x00, 0x20, (byte) 0xAD, 0x04, 0x00, 0x20, (byte) 0xC5, 0x04, 0x00, 0x20
@ -123,6 +128,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(8784, "9565,dfbd8fa,0,0,"); crcToVersion.put(8784, "9565,dfbd8fa,0,0,");
crcToVersion.put(16716, "9565,dfbd8faf42,0"); crcToVersion.put(16716, "9565,dfbd8faf42,0");
crcToVersion.put(54154, "9567,8b05506,0,0,"); crcToVersion.put(54154, "9567,8b05506,0,0,");
crcToVersion.put(15717, "15974,e61dd16,126");
// font // font
crcToVersion.put(61054, "8"); crcToVersion.put(61054, "8");
@ -141,7 +147,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
} }
return HuamiFirmwareType.RES; return HuamiFirmwareType.RES;
} }
if (ArrayUtils.startsWith(bytes, GPS_HEADER) || ArrayUtils.startsWith(bytes, GPS_HEADER2) || ArrayUtils.startsWith(bytes, GPS_HEADER3) || ArrayUtils.startsWith(bytes, GPS_HEADER4)) { if (ArrayUtils.startsWith(bytes, GPS_HEADER) || ArrayUtils.startsWith(bytes, GPS_HEADER2) || ArrayUtils.startsWith(bytes, GPS_HEADER3) || ArrayUtils.startsWith(bytes, GPS_HEADER4) || ArrayUtils.startsWith(bytes, GPS_HEADER5)) {
return HuamiFirmwareType.GPS; return HuamiFirmwareType.GPS;
} }
if (ArrayUtils.startsWith(bytes, GPS_ALMANAC_HEADER)) { if (ArrayUtils.startsWith(bytes, GPS_ALMANAC_HEADER)) {

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations; package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.widget.Toast; import android.widget.Toast;
@ -30,17 +32,21 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class);
@ -74,13 +80,7 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
GregorianCalendar sinceWhen = BLETypeConversions.createCalendar(); GregorianCalendar sinceWhen = BLETypeConversions.createCalendar();
sinceWhen.add(Calendar.DAY_OF_MONTH, -10); sinceWhen.add(Calendar.DAY_OF_MONTH, -10);
builder.write(characteristicFetch, BLETypeConversions.join(new byte[]{ startFetching(builder, AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS, sinceWhen);
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS},
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
builder.notify(characteristicActivityData, true);
builder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
} }
@Override @Override

View File

@ -50,12 +50,13 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(30045, "1.5.0.2"); crcToVersion.put(30045, "1.5.0.2");
crcToVersion.put(38254, "1.5.0.7"); crcToVersion.put(38254, "1.5.0.7");
crcToVersion.put(46985, "1.5.0.11"); crcToVersion.put(46985, "1.5.0.11");
crcToVersion.put(31330, "1.6.0.16");
// resources // resources
crcToVersion.put(54724, "1.2.0.8"); crcToVersion.put(54724, "1.2.0.8");
crcToVersion.put(52589, "1.3.0.4"); crcToVersion.put(52589, "1.3.0.4");
crcToVersion.put(34642, "1.3.0.8"); crcToVersion.put(34642, "1.3.0.8");
crcToVersion.put(25278, "1.4.0.12-1.5.0.11"); crcToVersion.put(25278, "1.4.0.12-1.6.0.16");
// font // font
crcToVersion.put(19775, "1"); crcToVersion.put(19775, "1");

View File

@ -63,6 +63,10 @@ public class MiBand3Support extends AmazfitBipSupport {
command[1] |= 0x04; command[1] |= 0x04;
command[5] = pos++; command[5] = pos++;
} }
if (pages.contains("activity")) {
command[1] |= 0x08;
command[6] = pos++;
}
if (pages.contains("more")) { if (pages.contains("more")) {
command[1] |= 0x10; command[1] |= 0x10;
command[7] = pos++; command[7] = pos++;

View File

@ -22,6 +22,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.support.annotation.CallSuper; import android.support.annotation.CallSuper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.widget.Toast;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -32,18 +33,22 @@ import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuamiOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuamiOperation;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
/** /**
* An operation that fetches activity data. For every fetch, a new operation must * An operation that fetches activity data. For every fetch, a new operation must
@ -137,6 +142,39 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation {
protected abstract void bufferActivityData(byte[] value); protected abstract void bufferActivityData(byte[] value);
protected void startFetching(TransactionBuilder builder, byte fetchType, GregorianCalendar sinceWhen) {
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
byte[] fetchBytes = BLETypeConversions.join(new byte[]{
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
fetchType},
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES));
builder.add(new AbstractGattListenerWriteAction(getQueue(), characteristicFetch, fetchBytes) {
@Override
protected boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
UUID characteristicUUID = characteristic.getUuid();
if (HuamiService.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) {
byte[] value = characteristic.getValue();
if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) {
handleActivityMetadata(value);
TransactionBuilder newBuilder = createTransactionBuilder(taskName + " Step 2");
newBuilder.notify(characteristicActivityData, true);
newBuilder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
try {
performImmediately(newBuilder);
} catch (IOException ex) {
GB.toast(getContext(), "Error fetching debug logs: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
}
return true;
} else {
handleActivityMetadata(value);
}
}
return false;
}
});
}
protected void handleActivityMetadata(byte[] value) { protected void handleActivityMetadata(byte[] value) {
if (value.length == 15) { if (value.length == 15) {
// first two bytes are whether our request was accepted // first two bytes are whether our request was accepted

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations; package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.widget.Toast; import android.widget.Toast;
@ -24,9 +26,11 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -34,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.Device;
@ -41,10 +46,14 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbstractGattListenerWriteAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
/** /**
* An operation that fetches activity data. For every fetch, a new operation must * An operation that fetches activity data. For every fetch, a new operation must
@ -68,11 +77,9 @@ public class FetchActivityOperation extends AbstractFetchOperation {
@Override @Override
protected void startFetching(TransactionBuilder builder) { protected void startFetching(TransactionBuilder builder) {
final String taskName = StringUtils.ensureNotNull(builder.getTaskName());
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { HuamiService.COMMAND_ACTIVITY_DATA_START_DATE, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); startFetching(builder, HuamiService.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY, sinceWhen);
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
builder.notify(characteristicActivityData, true);
builder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA});
} }
protected void handleActivityFetchFinish(boolean success) { protected void handleActivityFetchFinish(boolean success) {

View File

@ -71,14 +71,7 @@ public class FetchSportsDetailsOperation extends AbstractFetchOperation {
LOG.info("start " + getName()); LOG.info("start " + getName());
buffer = new ByteArrayOutputStream(1024); buffer = new ByteArrayOutputStream(1024);
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
startFetching(builder, AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_DETAILS, sinceWhen);
builder.write(characteristicFetch, BLETypeConversions.join(new byte[] {
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_DETAILS},
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
builder.notify(characteristicActivityData, true);
builder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA });
} }
@Override @Override

View File

@ -67,13 +67,7 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation {
protected void startFetching(TransactionBuilder builder) { protected void startFetching(TransactionBuilder builder) {
LOG.info("start" + getName()); LOG.info("start" + getName());
GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); GregorianCalendar sinceWhen = getLastSuccessfulSyncTime();
builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { startFetching(builder, AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES, sinceWhen);
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES},
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
builder.notify(characteristicActivityData, true);
builder.write(characteristicFetch, new byte[] { HuamiService.COMMAND_FETCH_DATA });
} }
@Override @Override

View File

@ -137,12 +137,6 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
} }
} }
private TransactionBuilder createTransactionBuilder(String task) {
TransactionBuilder builder = getSupport().createTransactionBuilder(task);
builder.setGattCallback(this);
return builder;
}
private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException { private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest md5 = MessageDigest.getInstance("MD5");
return md5.digest(message); return md5.digest(message);

View File

@ -389,7 +389,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
LOG.info("Attempting to set Fitness Goal..."); LOG.info("Attempting to set Fitness Goal...");
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
if (characteristic != null) { if (characteristic != null) {
int fitnessGoal = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, 10000); int fitnessGoal = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
transaction.write(characteristic, new byte[]{ transaction.write(characteristic, new byte[]{
MiBandService.COMMAND_SET_FITNESS_GOAL, MiBandService.COMMAND_SET_FITNESS_GOAL,
0, 0,

View File

@ -74,7 +74,11 @@ public abstract class RealtimeSamplesSupport {
} }
public synchronized void setSteps(int stepsPerMinute) { public synchronized void setSteps(int stepsPerMinute) {
this.steps = stepsPerMinute; if (stepsPerMinute == ActivitySample.NOT_MEASURED || stepsPerMinute >= 0) {
this.steps = stepsPerMinute;
} else {
this.steps = ActivitySample.NOT_MEASURED;
}
} }
/** /**

View File

@ -464,7 +464,7 @@ class PebbleIoThread extends GBDeviceIoThread {
mOutStream.flush(); mOutStream.flush();
} }
} catch (IOException e) { } catch (IOException e) {
LOG.error("Error writing.", e.getMessage()); LOG.error("Error writing.", e);
} }
try { try {
Thread.sleep(100); Thread.sleep(100);

View File

@ -497,7 +497,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
title = notificationSpec.title; title = notificationSpec.title;
} }
Long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
if (mFwMajor < 3) { if (mFwMajor < 3) {
ts += (SimpleTimeZone.getDefault().getOffset(ts)); ts += (SimpleTimeZone.getDefault().getOffset(ts));
} }
@ -514,7 +514,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies); notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies);
} else { } else {
// 1.x notification on FW 2.X // 1.x notification on FW 2.X
String[] parts = {title, notificationSpec.body, ts.toString(), subtitle}; String[] parts = {title, notificationSpec.body, String.valueOf(ts), subtitle};
// be aware that type is at this point always NOTIFICATION_EMAIL // be aware that type is at this point always NOTIFICATION_EMAIL
return encodeMessage(ENDPOINT_NOTIFICATION, NOTIFICATION_EMAIL, 0, parts); return encodeMessage(ENDPOINT_NOTIFICATION, NOTIFICATION_EMAIL, 0, parts);
} }
@ -636,14 +636,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attributes_count = 0; byte attributes_count = 0;
int length = 21 + 10 + actions_length; int length = 21 + 10 + actions_length;
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
attributes_count++;
length += (3 + s.getBytes().length);
} }
attributes_count++;
length += (3 + s.getBytes().length);
} }
// Encode Prefix // Encode Prefix
@ -667,19 +665,17 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attribute_id = 0; byte attribute_id = 0;
// Encode Pascal-Style Strings // Encode Pascal-Style Strings
if (parts != null) { for (String s : parts) {
for (String s : parts) { attribute_id++;
attribute_id++; if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
int partlength = s.getBytes().length;
if (partlength > 255) partlength = 255;
buf.put(attribute_id);
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
int partlength = s.getBytes().length;
if (partlength > 255) partlength = 255;
buf.put(attribute_id);
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
@ -981,14 +977,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attributes_count = 2; // icon byte attributes_count = 2; // icon
short attributes_length = (short) (11 + actions_length); short attributes_length = (short) (11 + actions_length);
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
attributes_count++;
attributes_length += (3 + s.getBytes().length);
} }
attributes_count++;
attributes_length += (3 + s.getBytes().length);
} }
short pin_length = (short) (NOTIFICATION_PIN_LENGTH + attributes_length); short pin_length = (short) (NOTIFICATION_PIN_LENGTH + attributes_length);
@ -1013,19 +1007,17 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attribute_id = 0; byte attribute_id = 0;
// Encode Pascal-Style Strings // Encode Pascal-Style Strings
if (parts != null) { for (String s : parts) {
for (String s : parts) { attribute_id++;
attribute_id++; if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
buf.put(attribute_id);
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
buf.put(attribute_id);
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
buf.put((byte) 4); // icon buf.put((byte) 4); // icon
@ -1116,14 +1108,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attributes_count = 3; byte attributes_count = 3;
short attributes_length = (short) (21 + actions_length); short attributes_length = (short) (21 + actions_length);
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
attributes_count++;
attributes_length += (3 + s.getBytes().length);
} }
attributes_count++;
attributes_length += (3 + s.getBytes().length);
} }
UUID uuid = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a43901"); UUID uuid = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a43901");
@ -1150,27 +1140,25 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte attribute_id = 0; byte attribute_id = 0;
// Encode Pascal-Style Strings // Encode Pascal-Style Strings
if (parts != null) { for (String s : parts) {
for (String s : parts) { attribute_id++;
attribute_id++; if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
if (attribute_id == 4) {
buf.put((byte) 11);
} else if (attribute_id == 5) {
buf.put((byte) 25);
} else if (attribute_id == 6) {
buf.put((byte) 26);
} else {
buf.put(attribute_id);
}
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
if (attribute_id == 4) {
buf.put((byte) 11);
} else if (attribute_id == 5) {
buf.put((byte) 25);
} else if (attribute_id == 6) {
buf.put((byte) 26);
} else {
buf.put(attribute_id);
}
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
buf.put((byte) 4); // icon buf.put((byte) 4); // icon
@ -1254,13 +1242,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
// Calculate length first // Calculate length first
short attributes_length = 0; short attributes_length = 0;
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
attributes_length += (2 + s.getBytes().length);
} }
attributes_length += (2 + s.getBytes().length);
} }
short pin_length = (short) (WEATHER_FORECAST_LENGTH + attributes_length); short pin_length = (short) (WEATHER_FORECAST_LENGTH + attributes_length);
@ -1280,17 +1266,15 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.putShort(attributes_length); buf.putShort(attributes_length);
// Encode Pascal-Style Strings // Encode Pascal-Style Strings
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { continue;
continue;
}
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
int partlength = s.getBytes().length;
if (partlength > 512) partlength = 512;
buf.putShort((short) partlength);
buf.put(s.getBytes(), 0, partlength);
} }
return encodeBlobdb(UUID_LOCATION, BLOBDB_INSERT, BLOBDB_WEATHER, buf.array()); return encodeBlobdb(UUID_LOCATION, BLOBDB_INSERT, BLOBDB_WEATHER, buf.array());
@ -1434,14 +1418,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
} else { } else {
// Calculate length first // Calculate length first
int length = LENGTH_PREFIX + 9; int length = LENGTH_PREFIX + 9;
if (parts != null) { for (String s : parts) {
for (String s : parts) { if (s == null || s.equals("")) {
if (s == null || s.equals("")) { length++; // encode null or empty strings as 0x00 later
length++; // encode null or empty strings as 0x00 later continue;
continue;
}
length += (1 + s.getBytes().length);
} }
length += (1 + s.getBytes().length);
} }
// Encode Prefix // Encode Prefix

View File

@ -24,6 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInf
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFmFrequency; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFmFrequency;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventLEDColor; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventLEDColor;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class Roidmi3Protocol extends RoidmiProtocol { public class Roidmi3Protocol extends RoidmiProtocol {
@ -40,7 +41,7 @@ public class Roidmi3Protocol extends RoidmiProtocol {
private static final byte[] COMMAND_GET_VOLTAGE = new byte[]{0x06, (byte) 0x81}; private static final byte[] COMMAND_GET_VOLTAGE = new byte[]{0x06, (byte) 0x81};
private static final byte[] COMMAND_SET_COLOR = new byte[]{0x02, 0x01, 0x00, 0x00, 0x00}; private static final byte[] COMMAND_SET_COLOR = new byte[]{0x02, 0x01, 0x00, 0x00, 0x00};
private static final byte[] COMMAND_SET_FREQUENCY = new byte[]{0x05, (byte) 0x81, 0x09, 0x64}; private static final byte[] COMMAND_SET_FREQUENCY = new byte[]{0x05, 0x01, 0x09, 0x64};
private static final byte[] COMMAND_DENOISE_ON = new byte[]{0x05, 0x06, 0x12}; private static final byte[] COMMAND_DENOISE_ON = new byte[]{0x05, 0x06, 0x12};
private static final byte[] COMMAND_DENOISE_OFF = new byte[]{0x05, 0x06, 0x00}; private static final byte[] COMMAND_DENOISE_OFF = new byte[]{0x05, 0x06, 0x00};
@ -58,7 +59,7 @@ public class Roidmi3Protocol extends RoidmiProtocol {
return null; return null;
} }
if (calcChecksum(res) != res[res.length - 2]) { if (calcChecksum(res) != res[res.length - 1]) {
LOG.info("Invalid response checksum"); LOG.info("Invalid response checksum");
return null; return null;
} }
@ -68,21 +69,22 @@ public class Roidmi3Protocol extends RoidmiProtocol {
return null; return null;
} }
if (res[1] != (byte) 0x81) { if (res[2] != (byte) 0x81) {
LOG.error("Unrecognized response" + GB.hexdump(res, 0, res.length)); LOG.warn("Potentially unsupported response: " + GB.hexdump(res, 0, res.length));
return null;
} }
if (res[1] == RESPONSE_VOLTAGE) { if (res[1] == RESPONSE_VOLTAGE) {
String voltageHex = GB.hexdump(res, 3, 2); String voltageHex = GB.hexdump(res, 3, 2);
float voltage = Float.valueOf(voltageHex) / 10.0f; float voltage = Float.valueOf(voltageHex) / 100.0f;
LOG.debug("Got voltage: " + voltage); LOG.debug("Got voltage: " + voltage);
GBDeviceEventBatteryInfo evBattery = new GBDeviceEventBatteryInfo(); GBDeviceEventBatteryInfo evBattery = new GBDeviceEventBatteryInfo();
evBattery.state = BatteryState.NO_BATTERY;
evBattery.level = GBDevice.BATTERY_UNKNOWN;
evBattery.voltage = voltage; evBattery.voltage = voltage;
return new GBDeviceEvent[]{evBattery}; return new GBDeviceEvent[]{evBattery};
} else if (res[1] == RESPONSE_COLOR) { } else if (res[1] == RESPONSE_COLOR) {
LOG.debug("Got color: " + GB.hexdump(res, 3, 3)); LOG.debug("Got color: #" + GB.hexdump(res, 3, 3));
int color = res[3] << 16 | res[4] << 8 | res[4]; int color = 0xFF000000 | ((res[3] << 16) & 0xFF0000) | ((res[4] << 8) & 0xFF00) | (res[5] & 0xFF);
GBDeviceEventLEDColor evColor = new GBDeviceEventLEDColor(); GBDeviceEventLEDColor evColor = new GBDeviceEventLEDColor();
evColor.color = color; evColor.color = color;
return new GBDeviceEvent[]{evColor}; return new GBDeviceEvent[]{evColor};
@ -94,7 +96,7 @@ public class Roidmi3Protocol extends RoidmiProtocol {
evFrequency.frequency = frequency; evFrequency.frequency = frequency;
return new GBDeviceEvent[]{evFrequency}; return new GBDeviceEvent[]{evFrequency};
} else { } else {
LOG.error("Unrecognized response" + GB.hexdump(res, 0, res.length)); LOG.error("Unrecognized response: " + GB.hexdump(res, 0, res.length));
return null; return null;
} }
} }
@ -103,9 +105,9 @@ public class Roidmi3Protocol extends RoidmiProtocol {
public byte[] encodeLedColor(int color) { public byte[] encodeLedColor(int color) {
byte[] cmd = COMMAND_SET_COLOR.clone(); byte[] cmd = COMMAND_SET_COLOR.clone();
cmd[2] = (byte) color; cmd[2] = (byte) (color >> 16);
cmd[3] = (byte) (color >> 8); cmd[3] = (byte) (color >> 8);
cmd[4] = (byte) (color >> 16); cmd[4] = (byte) color;
return encodeCommand(cmd); return encodeCommand(cmd);
} }
@ -144,7 +146,7 @@ public class Roidmi3Protocol extends RoidmiProtocol {
} }
public byte[] encodeGetVoltage() { public byte[] encodeGetVoltage() {
return COMMAND_GET_VOLTAGE; return encodeCommand(COMMAND_GET_VOLTAGE);
} }
public byte[] encodeDenoise(boolean enabled) { public byte[] encodeDenoise(boolean enabled) {

View File

@ -315,14 +315,12 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
music[5] = musicState; music[5] = musicState;
System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, music, 6, songtitle.getBytes(StandardCharsets.UTF_8).length); System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, music, 6, songtitle.getBytes(StandardCharsets.UTF_8).length);
music[music.length - 1] = ZeTimeConstants.CMD_END; music[music.length - 1] = ZeTimeConstants.CMD_END;
if (music != null) { try {
try { TransactionBuilder builder = performInitialized("setMusicStateInfo");
TransactionBuilder builder = performInitialized("setMusicStateInfo"); replyMsgToWatch(builder, music);
replyMsgToWatch(builder, music); builder.queue(getQueue());
builder.queue(getQueue()); } catch (IOException e) {
} catch (IOException e) { GB.toast(getContext(), "Error setting music state and info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
GB.toast(getContext(), "Error setting music state and info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
} }
} }
} }
@ -475,14 +473,12 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
} }
System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, music, 6, songtitle.getBytes(StandardCharsets.UTF_8).length); System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, music, 6, songtitle.getBytes(StandardCharsets.UTF_8).length);
music[music.length - 1] = ZeTimeConstants.CMD_END; music[music.length - 1] = ZeTimeConstants.CMD_END;
if (music != null) { try {
try { TransactionBuilder builder = performInitialized("setMusicStateInfo");
TransactionBuilder builder = performInitialized("setMusicStateInfo"); replyMsgToWatch(builder, music);
replyMsgToWatch(builder, music); builder.queue(getQueue());
builder.queue(getQueue()); } catch (IOException e) {
} catch (IOException e) { GB.toast(getContext(), "Error setting music state and info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
GB.toast(getContext(), "Error setting music state and info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
} }
} }
} }
@ -509,15 +505,12 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
CalendarEvent[14] = (byte) calendarEventSpec.title.getBytes(StandardCharsets.UTF_8).length; CalendarEvent[14] = (byte) calendarEventSpec.title.getBytes(StandardCharsets.UTF_8).length;
System.arraycopy(calendarEventSpec.title.getBytes(StandardCharsets.UTF_8), 0, CalendarEvent, 15, calendarEventSpec.title.getBytes(StandardCharsets.UTF_8).length); System.arraycopy(calendarEventSpec.title.getBytes(StandardCharsets.UTF_8), 0, CalendarEvent, 15, calendarEventSpec.title.getBytes(StandardCharsets.UTF_8).length);
CalendarEvent[CalendarEvent.length-1] = ZeTimeConstants.CMD_END; CalendarEvent[CalendarEvent.length-1] = ZeTimeConstants.CMD_END;
if(CalendarEvent != null) try {
{ TransactionBuilder builder = performInitialized("sendCalendarEvenr");
try { sendMsgToWatch(builder, CalendarEvent);
TransactionBuilder builder = performInitialized("sendCalendarEvenr"); builder.queue(getQueue());
sendMsgToWatch(builder, CalendarEvent); } catch (IOException e) {
builder.queue(getQueue()); GB.toast(getContext(), "Error sending calendar event: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
} catch (IOException e) {
GB.toast(getContext(), "Error sending calendar event: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
} }
} }
@ -587,15 +580,12 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
} }
System.arraycopy(weatherSpec.location.getBytes(StandardCharsets.UTF_8), 0, weather, 25, weatherSpec.location.getBytes(StandardCharsets.UTF_8).length); System.arraycopy(weatherSpec.location.getBytes(StandardCharsets.UTF_8), 0, weather, 25, weatherSpec.location.getBytes(StandardCharsets.UTF_8).length);
weather[weather.length-1] = ZeTimeConstants.CMD_END; weather[weather.length-1] = ZeTimeConstants.CMD_END;
if(weather != null) try {
{ TransactionBuilder builder = performInitialized("sendWeahter");
try { sendMsgToWatch(builder, weather);
TransactionBuilder builder = performInitialized("sendWeahter"); builder.queue(getQueue());
sendMsgToWatch(builder, weather); } catch (IOException e) {
builder.queue(getQueue()); GB.toast(getContext(), "Error sending weather: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
} catch (IOException e) {
GB.toast(getContext(), "Error sending weather: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
} }
} }
@ -758,8 +748,6 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
notification[5] = ZeTimeConstants.NOTIFICATION_SOCIAL; notification[5] = ZeTimeConstants.NOTIFICATION_SOCIAL;
break; break;
} }
if(notification != null)
{
try { try {
TransactionBuilder builder = performInitialized("sendNotification"); TransactionBuilder builder = performInitialized("sendNotification");
sendMsgToWatch(builder, notification); sendMsgToWatch(builder, notification);
@ -767,7 +755,6 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
} catch (IOException e) { } catch (IOException e) {
GB.toast(getContext(), "Error sending notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); GB.toast(getContext(), "Error sending notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
} }
}
} }
@Override @Override

View File

@ -91,7 +91,7 @@ public class GB {
text += ": " + context.getString(R.string.battery) + " " + device.getBatteryLevel() + "%"; text += ": " + context.getString(R.string.battery) + " " + device.getBatteryLevel() + "%";
} }
Boolean connected = device.isInitialized(); boolean connected = device.isInitialized();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(deviceName) builder.setContentTitle(deviceName)
.setTicker(deviceName + " - " + text) .setTicker(deviceName + " - " + text)

View File

@ -50,11 +50,12 @@ public class ImportExportSharedPreferences {
public static void exportToFile(SharedPreferences sharedPreferences, File outFile, public static void exportToFile(SharedPreferences sharedPreferences, File outFile,
Set<String> doNotExport) throws IOException { Set<String> doNotExport) throws IOException {
export(sharedPreferences, new FileWriter(outFile), doNotExport); try (FileWriter outputWriter = new FileWriter(outFile)) {
export(sharedPreferences, outputWriter, doNotExport);
}
} }
private static void export(SharedPreferences sharedPreferences, Writer writer,
public static void export(SharedPreferences sharedPreferences, Writer writer,
Set<String> doNotExport) throws IOException { Set<String> doNotExport) throws IOException {
XmlSerializer serializer = Xml.newSerializer(); XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(writer); serializer.setOutput(writer);
@ -75,11 +76,9 @@ public class ImportExportSharedPreferences {
serializer.attribute("", NAME, key); serializer.attribute("", NAME, key);
serializer.text(value); serializer.text(value);
serializer.endTag("", valueType); serializer.endTag("", valueType);
} }
serializer.endTag("", PREFERENCES); serializer.endTag("", PREFERENCES);
serializer.endDocument(); serializer.endDocument();
writer.close();
} }
public static boolean importFromFile(SharedPreferences sharedPreferences, File inFile) public static boolean importFromFile(SharedPreferences sharedPreferences, File inFile)

View File

@ -85,7 +85,11 @@ public class TimePreference extends DialogPreference {
time = getPersistedString(defaultValue.toString()); time = getPersistedString(defaultValue.toString());
} }
} else { } else {
time = defaultValue.toString(); if (defaultValue != null) {
time = defaultValue.toString();
} else {
time = "00:00";
}
} }
String[] pieces = time.split(":"); String[] pieces = time.split(":");

View File

@ -4,18 +4,28 @@
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity$PlaceholderFragment" tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity$PlaceholderFragment"
android:orientation="horizontal"> android:orientation="horizontal">
<com.github.mikephil.charting.charts.PieChart <LinearLayout
android:id="@+id/todaystepschart" android:layout_width="match_parent"
android:layout_width="fill_parent" android:layout_height="match_parent"
android:layout_height="fill_parent" android:layout_weight="40"
android:layout_weight="40"> android:orientation="vertical">
</com.github.mikephil.charting.charts.PieChart> <TextView
android:id="@+id/balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/todaystepschart"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="40" />
</LinearLayout>
<com.github.mikephil.charting.charts.BarChart <com.github.mikephil.charting.charts.BarChart
android:id="@+id/weekstepschart" android:id="@+id/weekstepschart"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="20" /> android:layout_weight="20" />
<!--<TextView--> <!--<TextView-->

View File

@ -30,6 +30,10 @@
android:layout_weight="20"> android:layout_weight="20">
</com.github.mikephil.charting.charts.PieChart> </com.github.mikephil.charting.charts.PieChart>
--> -->
<TextView
android:id="@+id/livechart_max_heart_rate"
grid:layout_columnSpan="2"
grid:layout_columnWeight="1" />
<com.github.mikephil.charting.charts.LineChart <com.github.mikephil.charting.charts.LineChart
android:id="@+id/livechart_steps_per_minute_history" android:id="@+id/livechart_steps_per_minute_history"

View File

@ -5,17 +5,21 @@
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity$PlaceholderFragment" tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity$PlaceholderFragment"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:id="@+id/balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.github.mikephil.charting.charts.PieChart <com.github.mikephil.charting.charts.PieChart
android:id="@+id/todaystepschart" android:id="@+id/todaystepschart"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="20"></com.github.mikephil.charting.charts.PieChart> android:layout_weight="20" />
<com.github.mikephil.charting.charts.BarChart <com.github.mikephil.charting.charts.BarChart
android:id="@+id/weekstepschart" android:id="@+id/weekstepschart"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="20" /> android:layout_weight="20" />
</LinearLayout> </LinearLayout>

View File

@ -5,7 +5,7 @@
<string name="action_settings">Nastavení</string> <string name="action_settings">Nastavení</string>
<string name="action_debug">Ladění</string> <string name="action_debug">Ladění</string>
<string name="action_quit">Ukončit</string> <string name="action_quit">Ukončit</string>
<string name="controlcenter_fetch_activity_data">Načíst data</string> <string name="controlcenter_fetch_activity_data">Synchronizovat</string>
<string name="controlcenter_start_sleepmonitor">Monitor spánku (alfa)</string> <string name="controlcenter_start_sleepmonitor">Monitor spánku (alfa)</string>
<string name="controlcenter_find_device">Najít zařízení</string> <string name="controlcenter_find_device">Najít zařízení</string>
<string name="controlcenter_take_screenshot">Pořídit snímek displeje</string> <string name="controlcenter_take_screenshot">Pořídit snímek displeje</string>
@ -44,31 +44,31 @@
<!--Strings related to FwAppInstaller--> <!--Strings related to FwAppInstaller-->
<string name="title_activity_fw_app_insaller">Instalátor FW/App</string> <string name="title_activity_fw_app_insaller">Instalátor FW/App</string>
<string name="fw_upgrade_notice">Instaluje firmvér %s do Mi Band místo stávajícího.</string> <string name="fw_upgrade_notice">Instaluje firmvér %s do Mi Band místo stávajícího.</string>
<string name="fw_multi_upgrade_notice">Instaluje firmvér %1$s a %2$s do Mi Band místo stávajícího.</string> <string name="fw_multi_upgrade_notice">Instaluje %1$s a %2$s firmvér, místo stávajícího.</string>
<string name="miband_firmware_known">Tento firmvér byl testován a je kompatibilní s Gadgetbridge.</string> <string name="miband_firmware_known">Tento firmvér byl testován a je známa jeho kompatibilita s Gadgetbridge.</string>
<string name="miband_firmware_unknown_warning">Tento firmvér nebyl testován a nemusí být kompatibilní s Gadgetbridge. <string name="miband_firmware_unknown_warning">Tento firmvér nebyl testován a nemusí být kompatibilní s Gadgetbridge.
\n \n
\nNedoporučujeme jej nahrávat do Mi Band!</string> \nNedoporučujeme jej nahrávat do Mi Band!</string>
<string name="miband_firmware_suggest_whitelist">Pokud budete prokračovat a vše bude fungovat běžně i potom, upozorněte vývojáře Gadgetbridge, aby označili tento firmvér: %s jako vyzkoušený</string> <string name="miband_firmware_suggest_whitelist">Chcete-li stále pokračovat a bude-li vše správně fungovat i po aktualizaci, oznamte vývojářům Gadgetbridge, aby označili tuto verzi firmvéru: %s jako funkční..</string>
<!--Strings related to Settings--> <!--Strings related to Settings-->
<string name="title_activity_settings">Nastavení</string> <string name="title_activity_settings">Nastavení</string>
<string name="pref_header_general">Základní nastavení</string> <string name="pref_header_general">Obecná nastavení</string>
<string name="pref_title_general_autoconnectonbluetooth">Připojit zařízení pokud je zapnutý BT</string> <string name="pref_title_general_autoconnectonbluetooth">Připojit Gadgetbridge k zařízení při zapnutí Bluetooth</string>
<string name="pref_title_general_autostartonboot">Spouštět automaticky</string> <string name="pref_title_general_autostartonboot">Spouštět automaticky</string>
<string name="pref_title_general_autoreconnect">Automatické znovupřipojení</string> <string name="pref_title_general_autoreconnect">Automatické znovupřipojení</string>
<string name="pref_title_audio_player">Preferovaný přehrávač zvuků</string> <string name="pref_title_audio_player">Preferovaný přehrávač hudby</string>
<string name="pref_default">Výchozí</string> <string name="pref_default">Výchozí</string>
<string name="pref_header_datetime">Datum a čas</string> <string name="pref_header_datetime">Datum a čas</string>
<string name="pref_title_datetime_syctimeonconnect">Nastavit čas</string> <string name="pref_title_datetime_syctimeonconnect">Synchronizovat čas</string>
<string name="pref_summary_datetime_syctimeonconnect">Nastavit čas během připojení a při změně času v Androidu</string> <string name="pref_summary_datetime_syctimeonconnect">Synchronizovat čas během připojení k zařízení a při změně času v Androidu</string>
<string name="pref_title_theme">Téma</string> <string name="pref_title_theme">Téma</string>
<string name="pref_theme_light">Světlé</string> <string name="pref_theme_light">Světlé</string>
<string name="pref_theme_dark">Tmavé</string> <string name="pref_theme_dark">Tmavé</string>
<string name="pref_title_language">Jazyk</string> <string name="pref_title_language">Jazyk</string>
<string name="pref_title_minimize_priority">Skrýt notifikace Gadgetbridge</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_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_summary_minimize_priority_on">Ikona ve stavové liště a notifikace na zamčeném displeji budou skryty</string>
<string name="pref_header_notifications">Notifikace</string> <string name="pref_header_notifications">Upozornění</string>
<string name="pref_title_notifications_repetitions">Opakování</string> <string name="pref_title_notifications_repetitions">Opakování</string>
<string name="pref_title_notifications_call">Volání</string> <string name="pref_title_notifications_call">Volání</string>
<string name="pref_title_notifications_sms">SMS</string> <string name="pref_title_notifications_sms">SMS</string>
@ -77,7 +77,7 @@
<string name="pref_title_notifications_generic">Přístup k notifikacím</string> <string name="pref_title_notifications_generic">Přístup k notifikacím</string>
<string name="pref_title_whenscreenon">Upozornění i při zapnuté obrazovce</string> <string name="pref_title_whenscreenon">Upozornění i při zapnuté obrazovce</string>
<string name="pref_title_notification_filter">Nerušit</string> <string name="pref_title_notification_filter">Nerušit</string>
<string name="pref_summary_notification_filter">Neposílat nechtěné notifikace v režimu Nerušit</string> <string name="pref_summary_notification_filter">Neposílat nechtěná upozornění v režimu Nerušit</string>
<string name="pref_title_transliteration">Přepsání diakritiky</string> <string name="pref_title_transliteration">Přepsání diakritiky</string>
<string name="pref_summary_transliteration">Toto zapněte, pokud vaše zařízení nepodporuje písma pro vás jazyk</string> <string name="pref_summary_transliteration">Toto zapněte, pokud vaše zařízení nepodporuje písma pro vás jazyk</string>
<string name="always">Vždy</string> <string name="always">Vždy</string>
@ -217,7 +217,7 @@
<string name="pref_screen_notification_profile_sms">Notifikace SMS</string> <string name="pref_screen_notification_profile_sms">Notifikace SMS</string>
<string name="pref_header_vibration_settings">Nastavení vibrací</string> <string name="pref_header_vibration_settings">Nastavení vibrací</string>
<string name="pref_screen_notification_profile_generic">Obecné notifikace</string> <string name="pref_screen_notification_profile_generic">Obecné notifikace</string>
<string name="pref_screen_notification_profile_email">Notifikace emailu</string> <string name="pref_screen_notification_profile_email">Upozornění emailu</string>
<string name="pref_screen_notification_profile_incoming_call">Notifikace volání</string> <string name="pref_screen_notification_profile_incoming_call">Notifikace volání</string>
<string name="pref_screen_notification_profile_generic_chat">Konverzace</string> <string name="pref_screen_notification_profile_generic_chat">Konverzace</string>
<string name="pref_screen_notification_profile_generic_navigation">Navigace</string> <string name="pref_screen_notification_profile_generic_navigation">Navigace</string>
@ -237,7 +237,7 @@
<string name="alarm_sat_short">So</string> <string name="alarm_sat_short">So</string>
<string name="alarm_smart_wakeup">Chytré buzení</string> <string name="alarm_smart_wakeup">Chytré buzení</string>
<string name="user_feedback_miband_set_alarms_failed">Nepodařilo se nastavit budík, zkuste to znovu!</string> <string name="user_feedback_miband_set_alarms_failed">Nepodařilo se nastavit budík, zkuste to znovu!</string>
<string name="user_feedback_miband_set_alarms_ok">Budík zaslán do zařízení!</string> <string name="user_feedback_miband_set_alarms_ok">Čas buzení odeslán do zařízení.</string>
<string name="chart_no_data_synchronize">Chybí data, provést stažení?</string> <string name="chart_no_data_synchronize">Chybí data, provést stažení?</string>
<string name="user_feedback_miband_activity_data_transfer">Stahnout data %1$s z %2$s</string> <string name="user_feedback_miband_activity_data_transfer">Stahnout data %1$s z %2$s</string>
<string name="miband_prefs_fitness_goal">Cílový počet kroků na den</string> <string name="miband_prefs_fitness_goal">Cílový počet kroků na den</string>
@ -279,7 +279,7 @@
<string name="pref_summary_dont_ack_transfers">Pokud není přenos dat potvrzen náramku, potom nebudou smazána. Užitečné pokud je GB používán dohromady s jinou aplikací.</string> <string name="pref_summary_dont_ack_transfers">Pokud není přenos dat potvrzen náramku, potom nebudou smazána. Užitečné pokud je GB používán dohromady s jinou aplikací.</string>
<string name="pref_summary_keep_data_on_device">Zachová data o aktivitě v Mi Band i po synchronizaci. Užitečné pokud je GB používán dohromady s jinou aplikací.</string> <string name="pref_summary_keep_data_on_device">Zachová data o aktivitě v Mi Band i po synchronizaci. Užitečné pokud je GB používán dohromady s jinou aplikací.</string>
<string name="pref_title_low_latency_fw_update">Použít režim z nízkým zpožděním pro aktualizace firmwaru</string> <string name="pref_title_low_latency_fw_update">Použít režim z nízkým zpožděním pro aktualizace firmwaru</string>
<string name="pref_summary_low_latency_fw_update">Může to pomoci u zařízení, kde aktualizace firmwaru selhala</string> <string name="pref_summary_low_latency_fw_update">Může to pomoci u zařízení, kde aktualizace firmwaru selhala.</string>
<string name="live_activity_steps_history">Historie kroků</string> <string name="live_activity_steps_history">Historie kroků</string>
<string name="live_activity_current_steps_per_minute">Aktuálně kroků/min</string> <string name="live_activity_current_steps_per_minute">Aktuálně kroků/min</string>
<string name="live_activity_total_steps">Celkem kroků</string> <string name="live_activity_total_steps">Celkem kroků</string>
@ -297,7 +297,7 @@
<string name="miband_prefs_reserve_alarm_calendar">Budíky pro nadcházející události</string> <string name="miband_prefs_reserve_alarm_calendar">Budíky pro nadcházející události</string>
<string name="miband_prefs_hr_sleep_detection">Použít sledování tepu pro zlepšení detekce spánku</string> <string name="miband_prefs_hr_sleep_detection">Použít sledování tepu pro zlepšení detekce spánku</string>
<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="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="miband2_prefs_dateformat">Formát data</string>
<string name="dateformat_time">Čas</string> <string name="dateformat_time">Čas</string>
<string name="dateformat_date_time">Čas a dátum</string> <string name="dateformat_date_time">Čas a dátum</string>
<string name="mi2_prefs_activate_display_on_lift">Zapnout displej při zvednutí</string> <string name="mi2_prefs_activate_display_on_lift">Zapnout displej při zvednutí</string>
@ -350,7 +350,7 @@
<string name="title_activity_vibration">Vibrace</string> <string name="title_activity_vibration">Vibrace</string>
<!--Strings related to Pebble Pairing Activity--> <!--Strings related to Pebble Pairing Activity-->
<string name="title_activity_pebble_pairing">Párování Pebble</string> <string name="title_activity_pebble_pairing">Párování Pebble</string>
<string name="pebble_pairing_hint">Dialog párování by se měl objevit na vašem zařízení s Androidem. Pokud se to nestane, zkontrolujte panel notifikací a potvrďte párování. Potom potvrďte párování na Pebble</string> <string name="pebble_pairing_hint">Dialog párování by se měl objevit na vašem zařízení s Androidem. Pokud se to nestane, zkontrolujte panel notifikací a potvrďte párování. Poté potvrďte párování na Pebble.</string>
<string name="weather_notification_label">Ujistěte se, že tento skin je povolen v aplikaci pro oznámení počasí pro získávání informací v Pebble.\n\nNení třeba nic nastavovat.\n\nAplikaci pro počasí Pebble je možné povolit ve správě aplikací.\n\nPodporované ciferníky ukáží počasí automaticky.</string> <string name="weather_notification_label">Ujistěte se, že tento skin je povolen v aplikaci pro oznámení počasí pro získávání informací v Pebble.\n\nNení třeba nic nastavovat.\n\nAplikaci pro počasí Pebble je možné povolit ve správě aplikací.\n\nPodporované ciferníky ukáží počasí automaticky.</string>
<string name="pref_title_setup_bt_pairing">Zapnout párování BT</string> <string name="pref_title_setup_bt_pairing">Zapnout párování BT</string>
<string name="pref_summary_setup_bt_pairing">Toto vypněte v případě problémů s připojením</string> <string name="pref_summary_setup_bt_pairing">Toto vypněte v případě problémů s připojením</string>
@ -435,13 +435,13 @@
\nDĚLÁTE TO NA VLASTNÍ NEBEZPEČÍ!</string> \nDĚLÁTE TO NA VLASTNÍ NEBEZPEČÍ!</string>
<string name="amazfitbip_firmware">"Firmware Amazfit Bipu %1$s"</string> <string name="amazfitbip_firmware">"Firmware Amazfit Bipu %1$s"</string>
<string name="controlcenter_connect">Připojit</string> <string name="controlcenter_connect">Připojit</string>
<string name="fw_upgrade_notice_amazfitcor">Chystáte se nainstalovat firmvér %s do vašeho Amazfit Cor. <string name="fw_upgrade_notice_amazfitcor">Chystáte se nainstalovat firmvér %s do vašeho Amazfit Cor.
\n
\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Vaše hodinky se po instalaci souboru .fw restartují.
\n
\nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve.
\n \n
\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Vaše hodinky se po instalaci souboru .fw restartují. \nPOKRAČUJTE NA VLASTNÍ NEBEZPEČÍ!</string>
\n
\nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve.
\n
\n NETESTOVÁNO, MŮŽE UMRTVIT VAŠE ZAŘÍZENÍ, DĚLÁTE TO NA VLASTNÍ NEBEZPEČÍ!</string>
<string name="pref_title_charts_swipe">Zapnout možnost potažení vlevo/vpravo v grafech aktivit</string> <string name="pref_title_charts_swipe">Zapnout možnost potažení vlevo/vpravo v grafech aktivit</string>
<string name="pref_title_weather">Počasí</string> <string name="pref_title_weather">Počasí</string>
@ -464,7 +464,7 @@
<string name="interval_thirty_minutes">Každých 30 minut</string> <string name="interval_thirty_minutes">Každých 30 minut</string>
<string name="interval_one_hour">Jednou za hodinu</string> <string name="interval_one_hour">Jednou za hodinu</string>
<string name="notif_export_failed_title">Export databáze selhal! Zkontrolujte svá nastavení.</string> <string name="notif_export_failed_title">Export databáze selhal! Zkontrolujte nastavení.</string>
<string name="automatic">Automaticky</string> <string name="automatic">Automaticky</string>
<string name="simplified_chinese">Zjednodušená Čínština</string> <string name="simplified_chinese">Zjednodušená Čínština</string>
<string name="traditional_chinese">Tradiční Čísština</string> <string name="traditional_chinese">Tradiční Čísština</string>
@ -473,7 +473,7 @@
<string name="activity_web_view">Aktivity Webový pohled</string> <string name="activity_web_view">Aktivity Webový pohled</string>
<string name="_pebble_watch_open_on_phone">Otevřít na telefonu</string> <string name="_pebble_watch_open_on_phone">Otevřít na Android zařízení</string>
<string name="_pebble_watch_mute">Ztlumit</string> <string name="_pebble_watch_mute">Ztlumit</string>
<string name="_pebble_watch_reply">Znova</string> <string name="_pebble_watch_reply">Znova</string>
@ -532,4 +532,116 @@
<string name="menuitem_compass">Kompas</string> <string name="menuitem_compass">Kompas</string>
<string name="menuitem_settings">Nastavení</string> <string name="menuitem_settings">Nastavení</string>
<string name="menuitem_alipay">Alipay</string> <string name="menuitem_alipay">Alipay</string>
<string name="controlcenter_change_led_color">Změnit barvu LED</string>
<string name="controlcenter_change_fm_frequency">Změnit FM frekvenci</string>
<string name="controlcenter_calibrate_device">Kalibrovat zařízení</string>
<string name="blacklist_all_for_notifications">Seznam všech zakázaných upozornění</string>
<string name="whitelist_all_for_notifications">Seznam všech povolených upozornění</string>
<string name="fw_upgrade_notice_miband3">Chystáte se nainstalovat firmvér %s do vašeho Mi Band 3.
\n
\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Vaše hodinky se po instalaci souboru .fw restartují.
\n
\nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve.
\n
\nNETESTOVÁNO, MŮŽE POŠKODIT VAŠE ZAŘÍZENÍ, POKRAČUJTE NA VLASTNÍ NEBEZPEČÍ!</string>
<string name="pref_title_notifications_timeout">Minimální doba mezi upozorněními</string>
<string name="pref_title_rtl">Zprava doleva</string>
<string name="pref_summary_rtl">Povolte, podporuje-li vaše zařízení jazyky zprava doleva</string>
<string name="pref_rtl_max_line_length">Maximální délka řádku pro zprava doleva</string>
<string name="pref_rtl_max_line_length_summary">Je-li zapnuta podpora pro zleva doprava, text je rozdělen do řádků. Změňte tuto hodnotu jsou-li řádky příliš dlouhé/krátké.</string>
<string name="preferences_id115_settings">ID115 nastavení</string>
<string name="prefs_screen_orientation">Orientace displeje</string>
<string name="pref_auto_fetch">Synchronizovat automaticky</string>
<string name="pref_auto_fetch_summary">Synchronizovat při odemčení obrazovky. Musí být nastaven zámek obrazovky!</string>
<string name="pref_auto_fetch_limit_fetches">Minimální doba mezi synchronizacemi</string>
<string name="pref_auto_fetch_limit_fetches_summary">Synchronizovat každých %d minut</string>
<string name="preferences_miband2_settings">Mi Band 2 nastavení</string>
<string name="preferences_miband3_settings">Mi Band 3 nastavení</string>
<string name="preferences_amazfitcor_settings">Amazfit Cor nastavení</string>s
<string name="horizontal">Na šířku</string>
<string name="vertical">Na výšku</string>
<string name="watch9_pairing_tap_hint">Po zavibrování stiskněte tlačítko, nebo se zařízením zatřeste.</string>
<string name="notif_battery_low">%1$s nízký stav baterie</string>
<string name="notif_battery_low_extended">%1$s baterie vybitá: %2$s</string>
<string name="lack_of_sleep">Nedostatek spánku: %1$s</string>
<string name="overslept">Příliš mnoho spánku: %1$s</string>
<string name="no_limit">Bez omezení</string>
<string name="seconds_5">5 vteřin</string>
<string name="seconds_10">10 vteřin</string>
<string name="seconds_20">20 vteřin</string>
<string name="seconds_30">30 vteřin</string>
<string name="minutes_1">1 minuta</string>
<string name="minutes_5">5 minut</string>
<string name="minutes_10">10 minut</string>
<string name="minutes_30">30 minut</string>
<string name="lack_of_step">Příliš málo kroků: %1$d</string>
<string name="overstep">Příliš mnoho kroků: %1$d</string>
<string name="live_activity_max_heart_rate">Aktuální / Maximální tepová frekvence: %1$d / %2$d</string>
<string name="you_slept">Spánek od %1$s do %2$s</string>
<string name="you_did_not_sleep">Bez spánku</string>
<string name="mi3_prefs_band_screen_unlock">Odemčení obrazovky zařízení</string>
<string name="mi3_prefs_band_screen_unlock_summary">Přejeďte prstem pro odemčení obrazovky zařízení</string>
<string name="mi3_prefs_night_mode">Noční režim</string>
<string name="mi3_prefs_night_mode_summary">Nižší automatická intenzita displeje v noci</string>
<string name="norwegian_bokmal">Norsky Bokmål</string>
<string name="russian">Rusky</string>
<string name="german">Německy</string>
<string name="italian">Italsky</string>
<string name="french">Francouzky</string>
<string name="polish">Polsky</string>
<string name="korean">Korejsky</string>
<string name="japanese">Japonsky</string>
<string name="activity_prefs_charts">Nastavení grafů</string>
<string name="activity_prefs_chart_max_heart_rate">Maximální tepová frekvence</string>
<string name="activity_prefs_chart_min_heart_rate">Minimální tepová frekvence</string>
<string name="ok">Ok</string>
<string name="mi3_night_mode_sunset">Při západu slunce</string>
<string name="controlcenter_start_activity_tracks">Záznamy aktivit</string>
<string name="activity_type_treadmill">Běhací pás</string>
<string name="devicetype_miband3">Mi Band 3</string>
<string name="devicetype_q8">Q8</string>
<string name="devicetype_mykronoz_zetime">MyKronoz ZeTime</string>
<string name="devicetype_id115">ID115</string>
<string name="devicetype_watch9">Watch 9</string>
<string name="devicetype_roidmi">Roidmi</string>
<string name="devicetype_roidmi3">Roidmi 3</string>
<string name="menuitem_notifications">Upozornění</string>
<string name="menuitem_music">Hudba</string>
<string name="menuitem_more">Více</string>
<string name="watch9_time_minutes">Minuty:</string>
<string name="watch9_time_hours">Hodiny:</string>
<string name="watch9_time_seconds">Vteřiny:</string>
<string name="watch9_calibration_hint">Nastavit čas aktuálně zobrazený na zařízení.</string>
<string name="watch9_calibration_button">Kalibrovat</string>
<string name="title_activity_watch9_pairing">Párování Watch 9</string>
<string name="title_activity_watch9_calibration">Kalibrace Watch 9</string>
<string name="pref_title_contextual_arabic">Kontextuální Arabština</string>
<string name="pref_summary_contextual_arabic">Zapnout podporu pro kontextuální arabštinu</string>
<string name="preferences_rtl_settings">Podpora zprava doleva</string>
<string name="share_log">Sdílet záznam</string>
<string name="share_log_warning">Prosím nezapomeňte, že ladící záznamy Gadgetbridge mohou obsahovat osobní informace, zahrnující například zdravotní data, identifikátory (MAC adresu), hudební preference a podobně. Tyto informace můžete vymazat před odesláním souboru do veřejného reportu.</string>
<string name="warning">Upozornění!</string>
<string name="no_data">Žádná data</string>
<string name="preferences_led_color">Barva LED</string>
<string name="preferences_fm_frequency">Frekvence FM</string>
<string name="pref_invalid_frequency_title">Neplatná frekvence</string>
<string name="pref_invalid_frequency_message">Nastavte frekvenci mezi 87.5 a 108.0</string>
<string name="language_and_region_prefs">Jazyk a místní nastavení</string>
</resources> </resources>

View File

@ -663,4 +663,47 @@
<string name="you_slept">"Κοιμηθήκατε %1$s έως %2$s"</string> <string name="you_slept">"Κοιμηθήκατε %1$s έως %2$s"</string>
<string name="you_did_not_sleep">Δεν κοιμηθήκατε</string> <string name="you_did_not_sleep">Δεν κοιμηθήκατε</string>
<string name="norwegian_bokmal">Νορβηγικά (Bokmål)</string> <string name="norwegian_bokmal">Νορβηγικά (Bokmål)</string>
</resources> <string name="controlcenter_change_led_color">Αλλαγή χρώματος LED ειδοποιήσεων</string>
<string name="controlcenter_change_fm_frequency">Αλλαγή συχνότητας FM</string>
<string name="mi3_prefs_night_mode">Λειτουργία \"νύχτας\"</string>
<string name="mi3_prefs_night_mode_summary">Μείωση φωτεινότητας της οθόνης του Mi band 3 κατά τη διάρκεια της νύχτας</string>
<string name="ok">Οκ</string>
<string name="mi3_night_mode_sunset">Στη δύση του ηλίου</string>
<string name="devicetype_roidmi">"Roidmi "</string>
<string name="devicetype_roidmi3">Roidmi3</string>
<string name="share_log">Αποστολή αρχείου καταγραφής</string>
<string name="share_log_warning">Παρακαλώ έχετε υπ\'όψιν σας ότι τα αρχεία καταγραφής του Gedgetbridge ενδέχεται να περιέχουν προσωπικά δεδομένα συμπεριλαμβανομένων δεδομένα υγείας, μοναδικά χαρακτηριστικά (όπως διευθύνσεις MAC), μουσικές επιλογές κλπ. προσπαθήστε να επεξεργαστείτε το αρχείο καταγραφής και να αφαιρέσετε αυτές τις πληροφορίες πριν το στείλετε για την αναφορά προβλήματος.</string>
<string name="warning">Προσοχή!</string>
<string name="preferences_led_color">Χρώμα LED</string>
<string name="preferences_fm_frequency">Συχνότητα FM</string>
<string name="pref_invalid_frequency_title">Μη έγκυρη συχνότητα</string>
<string name="pref_invalid_frequency_message">Παρακαλώ εισάγετε συχνότητα από 87.5 έως 108.0</string>
<string name="pref_title_rtl">"Από δεξιά προς αριστερά "</string>
<string name="pref_summary_rtl">"Ενεργοποιήστε αυτή την επιλογή αν η συσκευή σας δεν έχει υποστήριξη για γλώσσες που γράφονται από δεξιά "</string>
<string name="pref_rtl_max_line_length">Μέγιστο μήκος γραμμής στις γλώσσες που γράφονται από δεξιά</string>
<string name="pref_rtl_max_line_length_summary">Όταν η γραφή από δεξιά είναι ενεργοποιημένη , το κείμενο χωρίζεται σε γραμμές. Αλλάξτε αυτή τη τιμή αν οι γραμμές είναι πολύ μακριές ή κοντές.</string>
<string name="notif_battery_low">%1$s χαμηλή μπαταρία</string>
<string name="notif_battery_low_extended">%1$s χαμηλή μπαταρία: %2$s
\n</string>
<string name="lack_of_sleep">Έλλειψη ύπνου: %1$s</string>
<string name="overslept">Παραπάνω ύπνος: %1$s</string>
<string name="lack_of_step">Λιγότερα βήματα: %1$d</string>
<string name="overstep">Περισσότερα βήματα: %1$d</string>
<string name="live_activity_max_heart_rate">Τωρινοί / Μέγιστοι παλμοί: %1$d / %2$d</string>
<string name="activity_prefs_charts">"Ρυθμίσεις γραφήματος "</string>
<string name="activity_prefs_chart_max_heart_rate">Μέγιστοι παλμοί</string>
<string name="activity_prefs_chart_min_heart_rate">Ελάχιστοι παλμοί</string>
<string name="pref_title_contextual_arabic">Συμφραζόμενα Αραβικά</string>
<string name="pref_summary_contextual_arabic">Ενεργοποιήστε αυτή την επιλογή για υποστήριξη συμφραζόμενων Αραβικών</string>
<string name="preferences_rtl_settings">Υποστήριξη γλωσσών γραφής από τα δεξιά</string>
<string name="no_data">Χωρίς δεδομένα</string>
<string name="language_and_region_prefs">Ρυθμίσεις γλώσσας και περιοχής</string>
</resources>

View File

@ -62,7 +62,7 @@
<string name="pref_default">Par défaut</string> <string name="pref_default">Par défaut</string>
<string name="pref_header_datetime">Date et heure</string> <string name="pref_header_datetime">Date et heure</string>
<string name="pref_title_datetime_syctimeonconnect">Synchroniser l\'heure</string> <string name="pref_title_datetime_syctimeonconnect">Synchroniser l\'heure</string>
<string name="pref_summary_datetime_syctimeonconnect">Synchroniser l\'heure à l\'appareil lors de la connexion et lorsque l\'heure ou le fuseau horaire changent sur Android</string> <string name="pref_summary_datetime_syctimeonconnect">Synchroniser l\'heure sur l\'appareil lors de la connexion, et lorsque l\'heure ou le fuseau horaire changent sur Android</string>
<string name="pref_title_theme">Thème</string> <string name="pref_title_theme">Thème</string>
<string name="pref_theme_light">Clair</string> <string name="pref_theme_light">Clair</string>
<string name="pref_theme_dark">Sombre</string> <string name="pref_theme_dark">Sombre</string>
@ -79,7 +79,7 @@
<string name="pref_title_notifications_generic">Support des notifications génériques</string> <string name="pref_title_notifications_generic">Support des notifications génériques</string>
<string name="pref_title_whenscreenon">...y compris quand l\'écran est allumé</string> <string name="pref_title_whenscreenon">...y compris quand l\'écran est allumé</string>
<string name="pref_title_notification_filter">Ne Pas Déranger</string> <string name="pref_title_notification_filter">Ne Pas Déranger</string>
<string name="pref_summary_notification_filter">Arrêter lenvoi des notifications non désirées en mode Ne Pas Déranger</string> <string name="pref_summary_notification_filter">Les notifications non-sollicitées sont suspendues dans ce mode</string>
<string name="pref_title_transliteration">Transcription</string> <string name="pref_title_transliteration">Transcription</string>
<string name="pref_summary_transliteration">Activez ceci si votre appareil ne supporte pas la police de caractères</string> <string name="pref_summary_transliteration">Activez ceci si votre appareil ne supporte pas la police de caractères</string>
<string name="always">Toujours</string> <string name="always">Toujours</string>
@ -242,7 +242,7 @@
<string name="alarm_sat_short">Sam</string> <string name="alarm_sat_short">Sam</string>
<string name="alarm_smart_wakeup">Réveil intelligent</string> <string name="alarm_smart_wakeup">Réveil intelligent</string>
<string name="user_feedback_miband_set_alarms_failed">Une erreur s\'est produite lors du paramétrage des alarmes, veuillez réessayer !</string> <string name="user_feedback_miband_set_alarms_failed">Une erreur s\'est produite lors du paramétrage des alarmes, veuillez réessayer !</string>
<string name="user_feedback_miband_set_alarms_ok">Alarmes envoyées à l\'appareil !</string> <string name="user_feedback_miband_set_alarms_ok">Alarmes envoyées à l\'appareil.</string>
<string name="chart_no_data_synchronize">Aucune donnée. Synchroniser l\'appareil ?</string> <string name="chart_no_data_synchronize">Aucune donnée. Synchroniser l\'appareil ?</string>
<string name="user_feedback_miband_activity_data_transfer">Sur le point de transférer %1$s de données à partir de %2$s</string> <string name="user_feedback_miband_activity_data_transfer">Sur le point de transférer %1$s de données à partir de %2$s</string>
<string name="miband_prefs_fitness_goal">Objectif de pas par jour</string> <string name="miband_prefs_fitness_goal">Objectif de pas par jour</string>
@ -566,4 +566,72 @@ NOTE : la base de données sera bien évidement plus grande !</string>
<string name="menuitem_notifications">Notifications</string> <string name="menuitem_notifications">Notifications</string>
<string name="menuitem_music">Musique</string> <string name="menuitem_music">Musique</string>
<string name="menuitem_more">Suite</string> <string name="menuitem_more">Suite</string>
<string name="controlcenter_change_led_color">Changer la couleur de la LED</string>
<string name="controlcenter_change_fm_frequency">Changer la fréquence FM</string>
<string name="controlcenter_calibrate_device">Calibrer votre appareil</string>
<string name="pref_title_notifications_timeout">Temps minimum entre deux notifications</string>
<string name="pref_title_rtl">De droite à gauche</string>
<string name="pref_summary_rtl">Cocher ceci si votre appareil n\'est pas compatible avec les langues \"droite-à-gauche\"</string>
<string name="pref_rtl_max_line_length">Longueur max. d\'une ligne en mode Droite-à-Gauche</string>
<string name="watch9_pairing_tap_hint">Lorsque votre bracelet vibre, secouez-le ou pressez son bouton.</string>
<string name="notif_battery_low">%1$s niveau de batterie bas</string>
<string name="notif_battery_low_extended">%1$s niveau de batterie bas: %2$s</string>
<string name="lack_of_sleep">Manque de sommeil: %1$s</string>
<string name="overslept">Excès de sommeil: %1$s</string>
<string name="no_limit">Sans limite</string>
<string name="seconds_5">5 secondes</string>
<string name="seconds_10">10 secondes</string>
<string name="seconds_20">20 secondes</string>
<string name="seconds_30">30 secondes</string>
<string name="minutes_1">1 minute</string>
<string name="minutes_5">5 minutes</string>
<string name="minutes_10">10 minutes</string>
<string name="minutes_30">30 minutes</string>
<string name="lack_of_step">Manque d\'activité: %1$d</string>
<string name="overstep">Trop d\'activité: %1$d</string>
<string name="live_activity_max_heart_rate">Mesure cardiaque actuelle / maximum: %1$d / %2$d</string>
<string name="you_slept">Vous avez dormi de %1$s à %2$s</string>
<string name="you_did_not_sleep">Vous n\'avez pas dormi</string>
<string name="mi3_prefs_band_screen_unlock_summary">Glisser le doigt vers le haut pour débloquer l\'écran du bracelet</string>
<string name="mi3_prefs_night_mode">Mode nuit</string>
<string name="mi3_prefs_night_mode_summary">Diminuer la luminosité de l\'écran du bracelet automatiquement lorsqu\'il fait sombre</string>
<string name="german">Allemand</string>
<string name="italian">Italien</string>
<string name="french">Français</string>
<string name="polish">Polonais</string>
<string name="korean">Koréen</string>
<string name="japanese">Japonais</string>
<string name="activity_prefs_charts">Paramètres des graphiques</string>
<string name="activity_prefs_chart_max_heart_rate">Fréquence cardiaque maximum</string>
<string name="activity_prefs_chart_min_heart_rate">Fréquence cardiaque minimum</string>
<string name="ok">Ok</string>
<string name="mi3_night_mode_sunset">Au coucher du soleil</string>
<string name="devicetype_watch9">Watch 9</string>
<string name="watch9_time_minutes">Minutes:</string>
<string name="watch9_time_hours">Heures:</string>
<string name="watch9_time_seconds">Secondes:</string>
<string name="watch9_calibration_hint">Régler l\'heure que votre appareil indique actuellement.</string>
<string name="watch9_calibration_button">Calibrer</string>
<string name="title_activity_watch9_pairing">Appairage de la Watch 9</string>
<string name="title_activity_watch9_calibration">Calibration de la Watch 9</string>
<string name="preferences_rtl_settings">Support Droite-à-Gauche</string>
<string name="share_log">Partager les logs</string>
<string name="share_log_warning">Veuillez garder en tête que les fichiers logs de Gadgetbridge peuvent contenir des tas d\'informations personnelles, incluant entre autre des données relatives à la santé, des identifiants uniques (telles que des adresses MAC), des préférences musicales, etc. Pensez à modifier le fichier et retirer ces informations personnelles avant toute publication sur un rapport de bug public.</string>
<string name="warning">Attention!</string>
<string name="no_data">Pas de données</string>
<string name="preferences_led_color">Couleur de la LED</string>
<string name="preferences_fm_frequency">Fréquence FM</string>
<string name="pref_invalid_frequency_title">Fréquence non-valide</string>
<string name="pref_invalid_frequency_message">Veuillez introduire une fréquence entre 87.5 et 108.0</string>
<string name="language_and_region_prefs">Paramètres de langue et de région</string>
</resources> </resources>

View File

@ -626,4 +626,26 @@
<string name="preferences_fm_frequency">תדר FM</string> <string name="preferences_fm_frequency">תדר FM</string>
<string name="pref_invalid_frequency_title">תדר שגוי</string> <string name="pref_invalid_frequency_title">תדר שגוי</string>
<string name="pref_invalid_frequency_message">נא לבחור בתדר בין 87.5 לבין 108.0</string> <string name="pref_invalid_frequency_message">נא לבחור בתדר בין 87.5 לבין 108.0</string>
</resources> <string name="notif_battery_low">הסוללה של %1$s חלשה</string>
<string name="notif_battery_low_extended">הסוללה של %1$s חלשה: %2$s</string>
<string name="activity_prefs_charts">הגדרות תרשימים</string>
<string name="activity_prefs_chart_max_heart_rate">דופק מרבי</string>
<string name="activity_prefs_chart_min_heart_rate">דופק מזערי</string>
<string name="pref_title_rtl">מימין לשמאל</string>
<string name="pref_summary_rtl">יש להפעיל אפשרות זו אם ההתקן לא יכול להציג שפות מימין לשמאל</string>
<string name="pref_rtl_max_line_length">גודל שורה מרבי מימין לשמאל</string>
<string name="pref_rtl_max_line_length_summary">קיצור או הארכת השורות של טקסט מימין לשמאל</string>
<string name="pref_title_contextual_arabic">ערבית מחוברת</string>
<string name="pref_summary_contextual_arabic">יש להפעיל אפשרות זו כדי לתמוך בערבית מחוברת</string>
<string name="preferences_rtl_settings">תמיכה בטקסט מימין לשמאל</string>
<string name="language_and_region_prefs">הגדרות שפה ואיזור</string>
<string name="lack_of_sleep">מחסור בשינה: %1$s</string>
<string name="overslept">עודף שינה: %1$s</string>
<string name="lack_of_step">מחסור בצעדים: %1$d</string>
<string name="overstep">עודף צעדים: %1$d</string>
<string name="no_data">אין נתונים</string>
<string name="live_activity_max_heart_rate">דופק נוכחי / מרבי: %1$d / %2$d</string>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="app_name">Gadgetbridge</string>
<string name="preferences_amazfitbip_settings">Amazfit Bip Postavke</string>
<string name="preferences_amazfitcor_settings">Amazfit Cor Postavke</string>s
<string name="male">Muško</string>
<string name="female">Žensko</string>
<string name="other">Ostalo</string>
<string name="left">Lijevo</string>
</resources>

View File

@ -675,4 +675,26 @@
<string name="preferences_fm_frequency">FM-frekvens</string> <string name="preferences_fm_frequency">FM-frekvens</string>
<string name="pref_invalid_frequency_title">Ugyldig frekvens</string> <string name="pref_invalid_frequency_title">Ugyldig frekvens</string>
<string name="pref_invalid_frequency_message">Skriv inn en frekvens mellom 87.5 og 108.0</string> <string name="pref_invalid_frequency_message">Skriv inn en frekvens mellom 87.5 og 108.0</string>
<string name="pref_title_rtl">Høyre-til-venstre</string>
<string name="pref_summary_rtl">Skru på dette hvis din enhet ikke kan vise høyre til venstre-språk.</string>
<string name="pref_rtl_max_line_length">Maksimal linjelengde for høyre-til-venstre</string>
<string name="pref_rtl_max_line_length_summary">Strekker eller korter ned linjene høyre-til-venstre tekst inndeles i.</string>
<string name="notif_battery_low">%1$s batteri snart tomt</string>
<string name="notif_battery_low_extended">%1$s batteri snart tomt: %2$s</string>
<string name="lack_of_sleep">Søvnmangel: %1$s</string>
<string name="overslept">Sovet for lenge: %1$s</string>
<string name="lack_of_step">Ikke nok steg: %1$d</string>
<string name="overstep">For mange steg: %1$d</string>
<string name="live_activity_max_heart_rate">Nåværende / maksimal puls: %1$d /%2$d</string>
<string name="activity_prefs_charts">Diagraminnstillinger</string>
<string name="activity_prefs_chart_max_heart_rate">Makspuls</string>
<string name="activity_prefs_chart_min_heart_rate">Minimumspuls</string>
<string name="pref_title_contextual_arabic">Kontekstuell arabisk</string>
<string name="pref_summary_contextual_arabic">Skru på dette for kontekstuell arabisk støtte</string>
<string name="preferences_rtl_settings">Høyre-til-venstre -støtte</string>
<string name="no_data">Ingen data</string>
<string name="language_and_region_prefs">Språk og stedsinnstillinger</string>
</resources> </resources>

View File

@ -253,6 +253,8 @@
<string name="weeksleepchart_today_sleep_description">Сон сегодня, цель: %1$s</string> <string name="weeksleepchart_today_sleep_description">Сон сегодня, цель: %1$s</string>
<string name="weekstepschart_steps_a_week">Шагов в неделю</string> <string name="weekstepschart_steps_a_week">Шагов в неделю</string>
<string name="activity_sleepchart_activity_and_sleep">Ваши активность и сон</string> <string name="activity_sleepchart_activity_and_sleep">Ваши активность и сон</string>
<string name="lack_of_sleep">Недосып: %1$s</string>
<string name="overslept">Пересып: %1$s</string>
<string name="updating_firmware">Обновление прошивки…</string> <string name="updating_firmware">Обновление прошивки…</string>
<string name="fwapp_install_device_not_ready">Файл не может быть установлен, устройство не готово.</string> <string name="fwapp_install_device_not_ready">Файл не может быть установлен, устройство не готово.</string>
<string name="installhandler_firmware_name">%1$s: %2$s %3$s</string> <string name="installhandler_firmware_name">%1$s: %2$s %3$s</string>
@ -273,6 +275,8 @@
<string name="distance">Расстояние</string> <string name="distance">Расстояние</string>
<string name="liveactivity_live_activity">Жизненная активность</string> <string name="liveactivity_live_activity">Жизненная активность</string>
<string name="weeksteps_today_steps_description">Шагов сегодня, цель: %1$s</string> <string name="weeksteps_today_steps_description">Шагов сегодня, цель: %1$s</string>
<string name="lack_of_step">Не хватает: %1$d шагов</string>
<string name="overstep">Пройдено на %1$d шагов больше</string>
<string name="pref_title_dont_ack_transfer">Не подтверждать передачу данных об активности</string> <string name="pref_title_dont_ack_transfer">Не подтверждать передачу данных об активности</string>
<string name="pref_summary_dont_ack_transfers">Если данные об активности не будут переданы на устройство, оно не будет очищено. Полезно, если GB используется с другими приложениями.</string> <string name="pref_summary_dont_ack_transfers">Если данные об активности не будут переданы на устройство, оно не будет очищено. Полезно, если GB используется с другими приложениями.</string>
<string name="pref_summary_keep_data_on_device">Хранить данные о деятельности на Mi Band, даже после синхронизации. Полезно, если Mi Band используется совместно с другими приложениями.</string> <string name="pref_summary_keep_data_on_device">Хранить данные о деятельности на Mi Band, даже после синхронизации. Полезно, если Mi Band используется совместно с другими приложениями.</string>
@ -283,6 +287,7 @@
<string name="live_activity_total_steps">Всего шагов</string> <string name="live_activity_total_steps">Всего шагов</string>
<string name="live_activity_steps_per_minute_history">История шагов за минуту</string> <string name="live_activity_steps_per_minute_history">История шагов за минуту</string>
<string name="live_activity_start_your_activity">Начните вашу активность</string> <string name="live_activity_start_your_activity">Начните вашу активность</string>
<string name="live_activity_max_heart_rate">Текущая / максимальная ЧСС: %1$d / %2$d</string>
<string name="abstract_chart_fragment_kind_activity">Активность</string> <string name="abstract_chart_fragment_kind_activity">Активность</string>
<string name="abstract_chart_fragment_kind_light_sleep">Быстрый сон</string> <string name="abstract_chart_fragment_kind_light_sleep">Быстрый сон</string>
<string name="abstract_chart_fragment_kind_deep_sleep">Глубокий сон</string> <string name="abstract_chart_fragment_kind_deep_sleep">Глубокий сон</string>
@ -600,6 +605,7 @@
<string name="share_log">Поделиться лог-файлом</string> <string name="share_log">Поделиться лог-файлом</string>
<string name="share_log_warning">Файлы журнала Gadgetbridge могут содержать личную информацию, например уникальные идентификаторы: (устройство, MAC-адрес), предпочтения в музыке и т.д. Рассмотрите возможность редактирования файла и удаления этой информации перед отправкой файла общедоступное место.</string> <string name="share_log_warning">Файлы журнала Gadgetbridge могут содержать личную информацию, например уникальные идентификаторы: (устройство, MAC-адрес), предпочтения в музыке и т.д. Рассмотрите возможность редактирования файла и удаления этой информации перед отправкой файла общедоступное место.</string>
<string name="warning">Внимание!</string> <string name="warning">Внимание!</string>
<string name="no_data">Нет данных</string>
<string name="pref_title_notifications_timeout">Минимальный интервал между уведомлениями</string> <string name="pref_title_notifications_timeout">Минимальный интервал между уведомлениями</string>
<string name="no_limit">Без ограничений</string> <string name="no_limit">Без ограничений</string>
@ -628,4 +634,19 @@
<string name="preferences_fm_frequency">Частота УКВ</string> <string name="preferences_fm_frequency">Частота УКВ</string>
<string name="pref_invalid_frequency_title">Недопустимая частота</string> <string name="pref_invalid_frequency_title">Недопустимая частота</string>
<string name="pref_invalid_frequency_message">Выберите, пожалуйста, частоту в интервале 87.5—108</string> <string name="pref_invalid_frequency_message">Выберите, пожалуйста, частоту в интервале 87.5—108</string>
</resources> <string name="language_and_region_prefs">Язык и региональные настройки</string>
<string name="notif_battery_low">Аккумулятор %1$s почти разряжен</string>
<string name="notif_battery_low_extended">Аккумулятор %1$s почти разряжен: %2$s</string>
<string name="activity_prefs_charts">Настройки статистики активности</string>
<string name="activity_prefs_chart_max_heart_rate">Максимальный пульс</string>
<string name="activity_prefs_chart_min_heart_rate">Минимальный пульс</string>
<string name="pref_title_rtl">Справа налево</string>
<string name="pref_summary_rtl">Включите, если ваше устройство поддерживает ввод справа налево</string>
<string name="pref_rtl_max_line_length">Максимальная длина строки при вводе справа налево</string>
<string name="pref_rtl_max_line_length_summary">Изменяет длину строк, на которые разбит текст при вводе справа налево</string>
<string name="pref_title_contextual_arabic">Контекстные формы для арабского языка</string>
<string name="pref_summary_contextual_arabic">Включает поддержку контекстных форм арабского языка</string>
<string name="preferences_rtl_settings">Поддержка ввода справа налево</string>
</resources>

View File

@ -203,4 +203,6 @@
<string name="pref_title_location_latitude">Enlem</string> <string name="pref_title_location_latitude">Enlem</string>
<string name="pref_title_location_longitude">Boylam</string> <string name="pref_title_location_longitude">Boylam</string>
<string name="pref_title_location_keep_uptodate">Konumumu güncel tut</string> <string name="pref_title_location_keep_uptodate">Konumumu güncel tut</string>
<string name="controlcenter_change_led_color">"Türkçe "</string>
<string name="controlcenter_change_fm_frequency">Change FM Frequency</string>
</resources> </resources>

View File

@ -219,6 +219,7 @@
<string-array name="pref_miband3_display_items"> <string-array name="pref_miband3_display_items">
<item>@string/menuitem_notifications</item> <item>@string/menuitem_notifications</item>
<item>@string/menuitem_weather</item> <item>@string/menuitem_weather</item>
<item>@string/menuitem_activity</item>
<item>@string/menuitem_more</item> <item>@string/menuitem_more</item>
<item>@string/menuitem_status</item> <item>@string/menuitem_status</item>
<item>@string/heart_rate</item> <item>@string/heart_rate</item>
@ -227,6 +228,7 @@
<string-array name="pref_miband3_display_items_values"> <string-array name="pref_miband3_display_items_values">
<item>@string/p_menuitem_notifications</item> <item>@string/p_menuitem_notifications</item>
<item>@string/p_menuitem_weather</item> <item>@string/p_menuitem_weather</item>
<item>@string/p_menuitem_activity</item>
<item>@string/p_menuitem_more</item> <item>@string/p_menuitem_more</item>
<item>@string/p_menuitem_status</item> <item>@string/p_menuitem_status</item>
<item>@string/p_heart_rate</item> <item>@string/p_heart_rate</item>
@ -235,6 +237,7 @@
<string-array name="pref_miband3_display_items_default"> <string-array name="pref_miband3_display_items_default">
<item>@string/p_menuitem_notifications</item> <item>@string/p_menuitem_notifications</item>
<item>@string/p_menuitem_weather</item> <item>@string/p_menuitem_weather</item>
<item>@string/p_menuitem_activity</item>
<item>@string/p_menuitem_more</item> <item>@string/p_menuitem_more</item>
<item>@string/p_menuitem_status</item> <item>@string/p_menuitem_status</item>
<item>@string/p_heart_rate</item> <item>@string/p_heart_rate</item>

View File

@ -107,9 +107,9 @@
<string name="pref_summary_transliteration">Enable this if your device has no support for your language\'s font</string> <string name="pref_summary_transliteration">Enable this if your device has no support for your language\'s font</string>
<string name="pref_title_rtl">Right-To-Left</string> <string name="pref_title_rtl">Right-To-Left</string>
<string name="pref_summary_rtl">Enable this if your device has no support in right-to-left languages</string> <string name="pref_summary_rtl">Enable this if your device can not show right-to-left languages</string>
<string name="pref_rtl_max_line_length">Right-To-Left Max Line Length</string> <string name="pref_rtl_max_line_length">Right-To-Left Max Line Length</string>
<string name="pref_rtl_max_line_length_summary">When Right-To-Left is enable, the text is separated to lines. Change this value if the lines are to long/short.</string> <string name="pref_rtl_max_line_length_summary">Lengthens or shortens the lines Right-To-Left text is seperated into</string>
<string name="always">Always</string> <string name="always">Always</string>
<string name="when_screen_off">When screen is off</string> <string name="when_screen_off">When screen is off</string>
@ -408,6 +408,8 @@
<string name="weeksleepchart_today_sleep_description">Sleep today, target: %1$s</string> <string name="weeksleepchart_today_sleep_description">Sleep today, target: %1$s</string>
<string name="weekstepschart_steps_a_week">Steps per week</string> <string name="weekstepschart_steps_a_week">Steps per week</string>
<string name="activity_sleepchart_activity_and_sleep">Your activity and sleep</string> <string name="activity_sleepchart_activity_and_sleep">Your activity and sleep</string>
<string name="lack_of_sleep">Lack of sleep: %1$s</string>
<string name="overslept">Overslept: %1$s</string>
<string name="updating_firmware">Flashing firmware…</string> <string name="updating_firmware">Flashing firmware…</string>
<string name="fwapp_install_device_not_ready">File cannot be installed, device not ready.</string> <string name="fwapp_install_device_not_ready">File cannot be installed, device not ready.</string>
<string name="installhandler_firmware_name">%1$s: %2$s %3$s</string> <string name="installhandler_firmware_name">%1$s: %2$s %3$s</string>
@ -442,6 +444,8 @@
<string name="liveactivity_live_activity">Live activity</string> <string name="liveactivity_live_activity">Live activity</string>
<string name="weeksteps_today_steps_description">Steps today, target: %1$s</string> <string name="weeksteps_today_steps_description">Steps today, target: %1$s</string>
<string name="lack_of_step">Lack of steps: %1$d</string>
<string name="overstep">Overstep: %1$d</string>
<string name="pref_title_dont_ack_transfer">Do not ACK activity data transfer</string> <string name="pref_title_dont_ack_transfer">Do not ACK activity data transfer</string>
<string name="pref_summary_dont_ack_transfers">If the activity data are not acked to the band, they will not be cleared. Useful if GB is used together with other apps.</string> <string name="pref_summary_dont_ack_transfers">If the activity data are not acked to the band, they will not be cleared. Useful if GB is used together with other apps.</string>
<string name="pref_summary_keep_data_on_device">Will keep activity data on the Mi Band even after synchronization. Useful if GB is used together with other apps.</string> <string name="pref_summary_keep_data_on_device">Will keep activity data on the Mi Band even after synchronization. Useful if GB is used together with other apps.</string>
@ -453,6 +457,7 @@
<string name="live_activity_total_steps">Total steps</string> <string name="live_activity_total_steps">Total steps</string>
<string name="live_activity_steps_per_minute_history">Steps per minute history</string> <string name="live_activity_steps_per_minute_history">Steps per minute history</string>
<string name="live_activity_start_your_activity">Start your activity</string> <string name="live_activity_start_your_activity">Start your activity</string>
<string name="live_activity_max_heart_rate">Current / Max heart rate: %1$d / %2$d</string>
<string name="abstract_chart_fragment_kind_activity">Activity</string> <string name="abstract_chart_fragment_kind_activity">Activity</string>
<string name="abstract_chart_fragment_kind_light_sleep">Light sleep</string> <string name="abstract_chart_fragment_kind_light_sleep">Light sleep</string>
<string name="abstract_chart_fragment_kind_deep_sleep">Deep sleep</string> <string name="abstract_chart_fragment_kind_deep_sleep">Deep sleep</string>
@ -708,6 +713,7 @@
<string name="share_log">Share log</string> <string name="share_log">Share log</string>
<string name="share_log_warning">Please keep in mind Gadgetbridge log files may contain lots of personal information, including but not limited to health data, unique identifiers (such as a device MAC address), music preferences, etc. Consider editing the file and removing this information before sending the file to a public issue report.</string> <string name="share_log_warning">Please keep in mind Gadgetbridge log files may contain lots of personal information, including but not limited to health data, unique identifiers (such as a device MAC address), music preferences, etc. Consider editing the file and removing this information before sending the file to a public issue report.</string>
<string name="warning">Warning!</string> <string name="warning">Warning!</string>
<string name="no_data">No data</string>
<!-- LED Color --> <!-- LED Color -->
<string name="preferences_led_color">LED Color</string> <string name="preferences_led_color">LED Color</string>
@ -716,4 +722,5 @@
<string name="preferences_fm_frequency">FM Frequency</string> <string name="preferences_fm_frequency">FM Frequency</string>
<string name="pref_invalid_frequency_title">Invalid frequency</string> <string name="pref_invalid_frequency_title">Invalid frequency</string>
<string name="pref_invalid_frequency_message">Please enter a frequency between 87.5 and 108.0</string> <string name="pref_invalid_frequency_message">Please enter a frequency between 87.5 and 108.0</string>
<string name="language_and_region_prefs">Language and region settings</string>
</resources> </resources>

View File

@ -1,5 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<changelog> <changelog>
<release version="0.30.0" versioncode="138">
<change>Amazfit Bip + Mi Band 3: Support for right to left display (configurable) (#976)</change>
<change>Add Arabic, Bengali Farsi, Persian, Scandinavian transliteration</change>
<change>Add support for some Roidmi FM receivers</change>
<change>Mi Band 3: Allow enabling the "Workout" menu item</change>
<change>Mi Band 3: Support for night mode configuration</change>
<change>Huami devices: fix seldom activity/sports synchronization problem (#1264)</change>
<change>Preferences: Make minimum heart rate configurable (lower values will be disregarded)</change>
<change>Preferences: Configure minimum time between notifications</change>
<change>Preferences: Group language settings</change>
<change>Attempt to fix BLE connection issues on Samsung S devices</change>
<change>Week sleep and steps charts: display balance (actual value vs. desired value)</change>
<change>Live Activity: show current/maximum heart rate, display minute steps and total steps and more improvements</change>
<change>Live Activity: fix discrepancy between number of steps in Gadgetbridge and wearable device</change>
<change>Fix missing caller ID for incoming calls on Android 9</change>
<change>Support for easy sharing of log files via the Debug screen</change>
<change>Misc small bugfixes</change>
</release>
<release version="0.29.1" versioncode="137"> <release version="0.29.1" versioncode="137">
<change>Mi Band 3: Support setting language to to German, Italian, French, Polish, Japanese, Korean (read wiki)</change> <change>Mi Band 3: Support setting language to to German, Italian, French, Polish, Japanese, Korean (read wiki)</change>
<change>Mi Band 3: Support flashing latest RES files</change> <change>Mi Band 3: Support flashing latest RES files</change>

View File

@ -30,20 +30,55 @@
android:entryValues="@array/pref_theme_values" android:entryValues="@array/pref_theme_values"
android:defaultValue="@string/pref_theme_value_light" android:defaultValue="@string/pref_theme_value_light"
android:summary="%s" /> android:summary="%s" />
<ListPreference
android:defaultValue="default" <PreferenceScreen
android:entries="@array/pref_language_options" android:key="language_category"
android:entryValues="@array/pref_language_values" android:title="@string/language_and_region_prefs">
android:key="language" <ListPreference
android:summary="%s" android:defaultValue="default"
android:title="@string/pref_title_language" /> android:entries="@array/pref_language_options"
<ListPreference android:entryValues="@array/pref_language_values"
android:defaultValue="metric" android:key="language"
android:entries="@array/pref_entries_unit_system" android:summary="%s"
android:entryValues="@array/pref_values_unit_system" android:title="@string/pref_title_language" />
android:key="measurement_system" <ListPreference
android:summary="%s" android:defaultValue="metric"
android:title="@string/pref_title_unit_system" /> android:entries="@array/pref_entries_unit_system"
android:entryValues="@array/pref_values_unit_system"
android:key="measurement_system"
android:summary="%s"
android:title="@string/pref_title_unit_system" />
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="transliteration"
android:summary="@string/pref_summary_transliteration"
android:title="@string/pref_title_transliteration" />
<PreferenceScreen
android:title="@string/preferences_rtl_settings">
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="rtl"
android:summary="@string/pref_summary_rtl"
android:title="@string/pref_title_rtl" />
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="contextualArabic"
android:summary="@string/pref_summary_contextual_arabic"
android:title="@string/pref_title_contextual_arabic" />
<EditTextPreference
android:inputType="number"
android:key="rtl_max_line_length"
android:defaultValue="20"
android:maxLength="159"
android:title="@string/pref_rtl_max_line_length"
android:summary="@string/pref_rtl_max_line_length_summary"/>
</PreferenceScreen>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_title_weather"> <PreferenceScreen android:title="@string/pref_title_weather">
<EditTextPreference <EditTextPreference
android:key="weather_city" android:key="weather_city"
@ -202,39 +237,6 @@
android:title="@string/pref_title_notifications_timeout" android:title="@string/pref_title_notifications_timeout"
android:summary="%s" /> android:summary="%s" />
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="transliteration"
android:summary="@string/pref_summary_transliteration"
android:title="@string/pref_title_transliteration"
/>
<PreferenceScreen
android:title="@string/preferences_rtl_settings">
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="rtl"
android:summary="@string/pref_summary_rtl"
android:title="@string/pref_title_rtl"
/>
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="contextualArabic"
android:summary="@string/pref_summary_contextual_arabic"
android:title="@string/pref_title_contextual_arabic"
/>
<EditTextPreference
android:inputType="number"
android:key="rtl_max_line_length"
android:defaultValue="20"
android:maxLength="159"
android:title="@string/pref_rtl_max_line_length"
android:summary="@string/pref_rtl_max_line_length_summary"/>
</PreferenceScreen>
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox" android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
@ -598,21 +600,21 @@
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_header_development"> android:title="@string/pref_header_development">
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_force_protocol" android:key="pebble_force_protocol"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_forceprotocol" android:summary="@string/pref_summary_pebble_forceprotocol"
android:title="@string/pref_title_pebble_forceprotocol" /> android:title="@string/pref_title_pebble_forceprotocol" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_force_untested" android:key="pebble_force_untested"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_forceuntested" android:summary="@string/pref_summary_pebble_forceuntested"
android:title="@string/pref_title_pebble_forceuntested" /> android:title="@string/pref_title_pebble_forceuntested" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_force_le" android:key="pebble_force_le"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_forcele" android:summary="@string/pref_summary_pebble_forcele"
android:title="@string/pref_title_pebble_forcele" /> android:title="@string/pref_title_pebble_forcele" />
<EditTextPreference <EditTextPreference
@ -623,28 +625,28 @@
android:title="@string/pref_title_pebble_mtu_limit" android:title="@string/pref_title_pebble_mtu_limit"
android:summary="@string/pref_summary_pebble_mtu_limit" /> android:summary="@string/pref_summary_pebble_mtu_limit" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_gatt_clientonly" android:key="pebble_gatt_clientonly"
android:title="@string/pref_title_pebble_gatt_clientonly" android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_gatt_clientonly" /> android:summary="@string/pref_summary_pebble_gatt_clientonly"
android:title="@string/pref_title_pebble_gatt_clientonly" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_enable_applogs" android:key="pebble_enable_applogs"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_enable_applogs" android:summary="@string/pref_summary_pebble_enable_applogs"
android:title="@string/pref_title_pebble_enable_applogs" /> android:title="@string/pref_title_pebble_enable_applogs" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_always_ack_pebblekit" android:key="pebble_always_ack_pebblekit"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_always_ack_pebblekit" android:summary="@string/pref_summary_pebble_always_ack_pebblekit"
android:title="@string/pref_title_pebble_always_ack_pebblekit" /> android:title="@string/pref_title_pebble_always_ack_pebblekit" />
<CheckBoxPreference <CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="false" android:defaultValue="false"
android:key="pebble_enable_background_javascript"
android:dependency="pebble_force_untested" android:dependency="pebble_force_untested"
android:key="pebble_enable_background_javascript"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_pebble_enable_bgjs" android:summary="@string/pref_summary_pebble_enable_bgjs"
android:title="@string/pref_title_pebble_enable_bgjs" /> android:title="@string/pref_title_pebble_enable_bgjs" />
<EditTextPreference <EditTextPreference

View File

@ -6,7 +6,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.4' classpath 'com.android.tools.build:gradle:3.2.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -0,0 +1,16 @@
* Amazfit Bip + Mi Band 3: Support for right to left display (configurable) (#976)
* Add Arabic, Bengali Farsi, Persian, Scandinavian transliteration
* Add support for some Roidmi FM receivers
* Mi Band 3: Allow enabling the "Workout" menu item
* Mi Band 3: Support for night mode configuration
* Huami devices: fix seldom activity/sports synchronization problem (#1264)
* Preferences: Make minimum heart rate configurable (lower values will be disregarded)
* Preferences: Configure minimum time between notifications
* Preferences: Group language settings
* Attempt to fix BLE connection issues on Samsung S devices
* Week sleep and steps charts: display balance (actual value vs. desired value)
* Live Activity: show current/maximum heart rate, display minute steps and total steps and more improvements
* Live Activity: fix discrepancy between number of steps in Gadgetbridge and wearable device
* Fix missing caller ID for incoming calls on Android 9
* Support for easy sharing of log files via the Debug screen
* Misc small bugfixes

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip