From 48728cbb50a187ad68de81c7317b98d2bd26e600 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sat, 8 Apr 2017 22:26:12 +0200 Subject: [PATCH 01/40] Implement recyclerView in the AppBlackListActivity. This allows to implement a search functionality (in the future) --- .../activities/AppBlacklistActivity.java | 88 ++----------- .../adapter/AppBlacklistAdapter.java | 117 ++++++++++++++++++ .../main/res/layout/activity_appblacklist.xml | 13 +- .../main/res/layout/item_with_checkbox.xml | 20 +-- 4 files changed, 144 insertions(+), 94 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index ceba94ff9..bf4632686 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -22,36 +22,28 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; -import android.view.LayoutInflater; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; -import java.util.Comparator; import java.util.IdentityHashMap; -import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter; public class AppBlacklistActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class); + private AppBlacklistAdapter appBlacklistAdapter; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -68,77 +60,15 @@ public class AppBlacklistActivity extends GBActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_appblacklist); + RecyclerView appListView = (RecyclerView) findViewById(R.id.appListView); + appListView.setLayoutManager(new LinearLayoutManager(this)); - final PackageManager pm = getPackageManager(); + appBlacklistAdapter = new AppBlacklistAdapter(R.layout.item_with_checkbox, this); - final List packageList = pm.getInstalledApplications(PackageManager.GET_META_DATA); - ListView appListView = (ListView) findViewById(R.id.appListView); - - // sort the package list by label and blacklist status - nameMap = new IdentityHashMap<>(packageList.size()); - for (ApplicationInfo ai : packageList) { - CharSequence name = pm.getApplicationLabel(ai); - if (name == null) { - name = ai.packageName; - } - if (GBApplication.blacklist.contains(ai.packageName)) { - // sort blacklisted first by prefixing with a '!' - name = "!" + name; - } - nameMap.put(ai, name.toString()); - } - - Collections.sort(packageList, new Comparator() { - @Override - public int compare(ApplicationInfo ai1, ApplicationInfo ai2) { - final String s1 = nameMap.get(ai1); - final String s2 = nameMap.get(ai2); - return s1.compareTo(s2); - } - }); - - final ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item_with_checkbox, packageList) { - @Override - public View getView(int position, View view, ViewGroup parent) { - if (view == null) { - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.item_with_checkbox, parent, false); - } - - ApplicationInfo appInfo = packageList.get(position); - TextView deviceAppVersionAuthorLabel = (TextView) view.findViewById(R.id.item_details); - TextView deviceAppNameLabel = (TextView) view.findViewById(R.id.item_name); - ImageView deviceImageView = (ImageView) view.findViewById(R.id.item_image); - CheckBox checkbox = (CheckBox) view.findViewById(R.id.item_checkbox); - - deviceAppVersionAuthorLabel.setText(appInfo.packageName); - deviceAppNameLabel.setText(nameMap.get(appInfo)); - deviceImageView.setImageDrawable(appInfo.loadIcon(pm)); - - checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName)); - - return view; - } - }; - appListView.setAdapter(adapter); - - appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View v, int position, long id) { - String packageName = packageList.get(position).packageName; - CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox)); - checkBox.toggle(); - if (checkBox.isChecked()) { - GBApplication.addToBlacklist(packageName); - } else { - GBApplication.removeFromBlacklist(packageName); - } - } - }); + appListView.setAdapter(appBlacklistAdapter); IntentFilter filter = new IntentFilter(); filter.addAction(GBApplication.ACTION_QUIT); - LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java new file mode 100644 index 000000000..813e1e32a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java @@ -0,0 +1,117 @@ +package nodomain.freeyourgadget.gadgetbridge.adapter; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.Collections; +import java.util.Comparator; +import java.util.IdentityHashMap; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; + +public class AppBlacklistAdapter extends RecyclerView.Adapter { + + private final List applicationInfoList; + private final int mLayoutId; + private final Context mContext; + private final PackageManager mPm; + private final IdentityHashMap mNameMap; + + + public AppBlacklistAdapter(int layoutId, Context context) { + mLayoutId = layoutId; + mContext = context; + mPm = context.getPackageManager(); + + applicationInfoList = mPm.getInstalledApplications(PackageManager.GET_META_DATA); + + // sort the package list by label and blacklist status + mNameMap = new IdentityHashMap(applicationInfoList.size()); + for (ApplicationInfo ai : applicationInfoList) { + CharSequence name = mPm.getApplicationLabel(ai); + if (name == null) { + name = ai.packageName; + } + if (GBApplication.blacklist.contains(ai.packageName)) { + // sort blacklisted first by prefixing with a '!' + name = "!" + name; + } + mNameMap.put(ai, name.toString()); + } + + Collections.sort(applicationInfoList, new Comparator() { + @Override + public int compare(ApplicationInfo ai1, ApplicationInfo ai2) { + final String s1 = mNameMap.get(ai1); + final String s2 = mNameMap.get(ai2); + return s1.compareTo(s2); + } + }); + + } + + @Override + public AppBlacklistAdapter.AppBLViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(mContext).inflate(mLayoutId, parent, false); + return new AppBLViewHolder(view); + } + + @Override + public void onBindViewHolder(AppBlacklistAdapter.AppBLViewHolder holder, int position) { + final ApplicationInfo appInfo = applicationInfoList.get(position); + + holder.deviceAppVersionAuthorLabel.setText(appInfo.packageName); + holder.deviceAppNameLabel.setText(mNameMap.get(appInfo)); + holder.deviceImageView.setImageDrawable(appInfo.loadIcon(mPm)); + + holder.checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName)); + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox)); + checkBox.toggle(); + if (checkBox.isChecked()) { + GBApplication.addToBlacklist(appInfo.packageName); + } else { + GBApplication.removeFromBlacklist(appInfo.packageName); + } + } + }); + + } + + @Override + public int getItemCount() { + return applicationInfoList.size(); + } + + public class AppBLViewHolder extends RecyclerView.ViewHolder { + + final CheckBox checkbox; + final ImageView deviceImageView; + final TextView deviceAppVersionAuthorLabel; + final TextView deviceAppNameLabel; + + AppBLViewHolder(View itemView) { + super(itemView); + + checkbox = (CheckBox) itemView.findViewById(R.id.item_checkbox); + deviceImageView = (ImageView) itemView.findViewById(R.id.item_image); + deviceAppVersionAuthorLabel = (TextView) itemView.findViewById(R.id.item_details); + deviceAppNameLabel = (TextView) itemView.findViewById(R.id.item_name); + } + + } + +} diff --git a/app/src/main/res/layout/activity_appblacklist.xml b/app/src/main/res/layout/activity_appblacklist.xml index 5ad2fbef9..08f0445b4 100644 --- a/app/src/main/res/layout/activity_appblacklist.xml +++ b/app/src/main/res/layout/activity_appblacklist.xml @@ -2,17 +2,14 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" tools:context="nodomain.freeyourgadget.gadgetbridge.activities.AppBlacklistActivity"> - + android:layout_centerHorizontal="true" + android:divider="@null" /> diff --git a/app/src/main/res/layout/item_with_checkbox.xml b/app/src/main/res/layout/item_with_checkbox.xml index c166b0aa6..45ffc8ff2 100644 --- a/app/src/main/res/layout/item_with_checkbox.xml +++ b/app/src/main/res/layout/item_with_checkbox.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/activatedBackgroundIndicator" - android:padding="8dp"> + android:minHeight="60dp"> @@ -19,7 +21,12 @@ android:id="@+id/item_image" android:layout_width="48dp" android:layout_height="48dp" - android:layout_toRightOf="@+id/item_checkbox" /> + android:layout_centerVertical="true" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_toEndOf="@+id/item_checkbox" + android:paddingBottom="8dp" + android:paddingTop="8dp" /> + android:padding="8dp"> From c2f83fa85784ee8fee42cdce29ae5c0281552300 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 22:36:40 +0200 Subject: [PATCH 02/40] Add changelog entries --- CHANGELOG.md | 7 ++++++- app/src/main/res/xml/changelog_master.xml | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c201042ab..c9ade88a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ ###Version next * Applied some material design guidelines to Charts and (pebble) app management -* Pebble: improve compatiblity with watchapp configuration pages +* Changed colours: deep sleep is now dark blue, light sleep is now light blue +* Support for exporting and importing of preferences in addition to the database +* Visual improvements of the pie charts +* Pebble: improve compatibility with watch app configuration pages +* HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) +* HPlus: display battery state and warn on low battery ###Version 0.18.4 * Mi Band 2: Display realtime steps in Live Activity diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index cf2811de8..f15918ae0 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,13 @@ - Applied some material design guidelines to Charts and (pebble) app management - - Pebble: improve compatiblity with watchapp configuration pages + Applied some material design guidelines to Charts and (pebble) app management + Changed colours: deep sleep is now dark blue, light sleep is now light blue + Support for exporting and importing of preferences in addition to the database + Visual improvements of the pie charts + Pebble: improve compatibility with watch app configuration pages + HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) + HPlus: display battery state and warn on low battery From 2feb3bed47fbe7ecaad73554a359558b7b32d899 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 22:52:22 +0200 Subject: [PATCH 03/40] Reduce animation time from 350ms to 250ms (seemed a bit laggy to me) --- .../gadgetbridge/activities/charts/AbstractChartFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 41ec58539..3e7faeffe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -93,7 +93,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; * shift the date by one day. */ public abstract class AbstractChartFragment extends AbstractGBFragment { - protected final int ANIM_TIME = 350; + protected final int ANIM_TIME = 250; private static final Logger LOG = LoggerFactory.getLogger(AbstractChartFragment.class); From 155ce5be026b93e1e329d050709b03773a32ec14 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 8 Apr 2017 22:58:58 +0200 Subject: [PATCH 04/40] Font size bar chart (#645) Increas font size bar chart small fix that improves the readability of the values on bar charts --- .../activities/charts/AbstractWeekChartFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java index 0dfef9b82..978826b05 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -115,6 +115,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { BarData barData = new BarData(set); barData.setValueTextColor(Color.GRAY); //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false); + barData.setValueTextSize(10f); LimitLine target = new LimitLine(mTargetValue); barChart.getAxisLeft().removeAllLimitLines(); From 4a4a1e25df99941acc60332a4e5d8ac65a187540 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 23:16:33 +0200 Subject: [PATCH 05/40] Properly format the sleep goal as a duration, not as a time Also suppress trailing zeros, e.g. display 8h instead of 8h 0m --- .../gadgetbridge/activities/charts/WeekSleepChartFragment.java | 2 +- .../freeyourgadget/gadgetbridge/util/DateTimeUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java index 29ef5a969..f0ea551fe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -44,7 +44,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment { @Override String getPieDescription(int targetValue) { - return getString(R.string.weeksleepchart_today_sleep_description, DateTimeUtils.minutesToHHMM(targetValue)); + return getString(R.string.weeksleepchart_today_sleep_description, DateTimeUtils.formatDurationHoursMinutes(targetValue, TimeUnit.MINUTES)); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java index c0d6d8e19..068024b32 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java @@ -54,7 +54,7 @@ public class DateTimeUtils { DurationFormatter df = DurationFormatter.Builder.SYMBOLS .maximum(TimeUnit.DAYS) .minimum(TimeUnit.MINUTES) - .suppressZeros(DurationFormatter.SuppressZeros.LEADING) + .suppressZeros(DurationFormatter.SuppressZeros.LEADING, DurationFormatter.SuppressZeros.TRAILING) .maximumAmountOfUnitsToShow(2) .build(); return df.format(duration, unit); From f658059d20444d8106aef138eef7554a889ff3ce Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 8 Apr 2017 23:18:47 +0200 Subject: [PATCH 06/40] Pebble: really disable battery reporting --- .../service/devices/pebble/DatalogSessionAnalytics.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java index 419b68b69..80659c5dc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java @@ -58,8 +58,8 @@ class DatalogSessionAnalytics extends DatalogSession { LOG.info("Battery reading for TS " + messageTS + " is: " + reportedMilliVolts + " milliVolts, mapped to percentage: " + milliVoltstoPercentage(reportedMilliVolts)); - if (messageTS > 0 && reportedMilliVolts < 5000) { //some safety checks -// mGBDeviceEventBatteryInfo.state = BatteryState.BATTERY_NORMAL; //uncoomment this to show the battery status in the control center + if (false && (messageTS > 0 && reportedMilliVolts < 5000)) { //some safety checks, always disabled for now + mGBDeviceEventBatteryInfo.state = BatteryState.BATTERY_NORMAL; mGBDeviceEventBatteryInfo.level = milliVoltstoPercentage(reportedMilliVolts); return new GBDeviceEvent[]{mGBDeviceEventBatteryInfo, null}; From 4519f35ff1f475346dc8c2b99ea62bc7056b1443 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 23:36:16 +0200 Subject: [PATCH 07/40] Bump up versions of some dependencies --- app/build.gradle | 4 ++-- .../freeyourgadget/gadgetbridge/test/LoggingTest.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5a35822ef..729c21076 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,14 +69,14 @@ dependencies { compile 'com.android.support:recyclerview-v7:25.3.1' compile 'com.android.support:support-v4:25.3.1' compile 'com.android.support:design:25.3.1' - compile 'com.github.tony19:logback-android-classic:1.1.1-4' + compile 'com.github.tony19:logback-android-classic:1.1.1-6' compile 'org.slf4j:slf4j-api:1.7.7' compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' compile 'net.e175.klaus:solarpositioning:0.0.9' compile 'com.github.freeyourgadget:greendao:1998d7cd2d21f662c6044f6ccf3b3a251bbad341' - compile 'org.apache.commons:commons-lang3:3.4' + compile 'org.apache.commons:commons-lang3:3.5' // compile project(":DaoCore") } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java index e7062bfd6..bd586669c 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LoggingTest.java @@ -32,6 +32,7 @@ public class LoggingTest extends TestBase { } }; + @Override @After public void tearDown() { assertTrue(FileUtils.deleteRecursively(getLogFilesDir())); From 39c7c1aae39fad8c769516efd40d0c60e6925c77 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 23:42:29 +0200 Subject: [PATCH 08/40] Update robolectric to 3.3.2 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 729c21076..1bac8399b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,7 +61,7 @@ dependencies { // testCompile 'ch.qos.logback:logback-core:1.1.3' testCompile 'junit:junit:4.12' testCompile "org.mockito:mockito-core:1.9.5" - testCompile "org.robolectric:robolectric:3.2.2" + testCompile "org.robolectric:robolectric:3.3.2" compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:25.3.1' From bb98910e1c3f00db51202aa52ba8d3ceded28a9c Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 8 Apr 2017 23:59:00 +0200 Subject: [PATCH 09/40] Update gradle to 3.4.1, pmd to 5.5.5 --- app/build.gradle | 2 +- .../gadgetbridge/test/LanguageUtilsTest.java | 21 ++++++++++++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1bac8399b..44f50e4ee 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,7 +53,7 @@ android { } pmd { - toolVersion = '5.5.1' + toolVersion = '5.5.5' } dependencies { diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java index ad5b8bb1a..b4247cf2c 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/LanguageUtilsTest.java @@ -37,13 +37,24 @@ public class LanguageUtilsTest extends TestBase { @Test public void testTransliterateOption() throws Exception { + setDefaultTransliteration(); assertFalse("Transliteration option fail! Expected 'Off' by default, but result is 'On'", LanguageUtils.transliterate()); - SharedPreferences settings = GBApplication.getPrefs().getPreferences(); - SharedPreferences.Editor editor = settings.edit(); - editor.putBoolean("transliteration", true); - editor.apply(); - + enableTransliteration(true); assertTrue("Transliteration option fail! Expected 'On', but result is 'Off'", LanguageUtils.transliterate()); } + + private void setDefaultTransliteration() { + SharedPreferences settings = GBApplication.getPrefs().getPreferences(); + SharedPreferences.Editor editor = settings.edit(); + editor.remove("transliteration"); + editor.apply(); + } + + private void enableTransliteration(boolean enable) { + SharedPreferences settings = GBApplication.getPrefs().getPreferences(); + SharedPreferences.Editor editor = settings.edit(); + editor.putBoolean("transliteration", enable); + editor.apply(); + } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a36803658..cdb0e23b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip From a77ff03ca54033be7cc47b2c62239bf2584c82ae Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 9 Apr 2017 00:21:43 +0200 Subject: [PATCH 10/40] Add filter functionality to the app blacklist activity --- CHANGELOG.md | 1 + .../activities/AppBlacklistActivity.java | 17 ++++++ .../adapter/AppBlacklistAdapter.java | 60 ++++++++++++++++++- .../main/res/layout/activity_appblacklist.xml | 12 +++- .../main/res/layout/item_with_checkbox.xml | 4 +- app/src/main/res/xml/changelog_master.xml | 1 + 6 files changed, 90 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9ade88a3..730debff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Changed colours: deep sleep is now dark blue, light sleep is now light blue * Support for exporting and importing of preferences in addition to the database * Visual improvements of the pie charts +* Add filter by name in the App blacklist activity * Pebble: improve compatibility with watch app configuration pages * HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) * HPlus: display battery state and warn on low battery diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index bf4632686..b83956cd6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -27,6 +27,7 @@ import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SearchView; import android.view.MenuItem; import org.slf4j.Logger; @@ -67,6 +68,22 @@ public class AppBlacklistActivity extends GBActivity { appListView.setAdapter(appBlacklistAdapter); + SearchView searchView = (SearchView) findViewById(R.id.appListViewSearch); + searchView.setIconifiedByDefault(false); + searchView.setIconified(false); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + appBlacklistAdapter.getFilter().filter(newText); + return true; + } + }); + IntentFilter filter = new IntentFilter(); filter.addAction(GBApplication.ACTION_QUIT); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java index 813e1e32a..b2b2a3c12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java @@ -8,9 +8,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.IdentityHashMap; @@ -19,14 +22,15 @@ import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -public class AppBlacklistAdapter extends RecyclerView.Adapter { +public class AppBlacklistAdapter extends RecyclerView.Adapter implements Filterable { - private final List applicationInfoList; + private List applicationInfoList; private final int mLayoutId; private final Context mContext; private final PackageManager mPm; private final IdentityHashMap mNameMap; + private ApplicationFilter applicationFilter; public AppBlacklistAdapter(int layoutId, Context context) { mLayoutId = layoutId; @@ -96,6 +100,13 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter originalList; + private final List filteredList; + + private ApplicationFilter(AppBlacklistAdapter adapter, List originalList) { + super(); + this.originalList = new ArrayList<>(originalList); + this.filteredList = new ArrayList<>(); + this.adapter = adapter; + } + + @Override + protected Filter.FilterResults performFiltering(CharSequence filter) { + filteredList.clear(); + final Filter.FilterResults results = new Filter.FilterResults(); + + if (filter == null || filter.length() == 0) + filteredList.addAll(originalList); + else { + final String filterPattern = filter.toString().toLowerCase().trim(); + + for (ApplicationInfo ai : originalList) { + CharSequence name = mPm.getApplicationLabel(ai); + if (((String) name).contains(filterPattern) || + (ai.packageName.contains(filterPattern))) { + filteredList.add(ai); + } + } + } + + results.values = filteredList; + results.count = filteredList.size(); + return results; + } + + @Override + protected void publishResults(CharSequence charSequence, Filter.FilterResults filterResults) { + adapter.applicationInfoList.clear(); + adapter.applicationInfoList.addAll((List) filterResults.values); + adapter.notifyDataSetChanged(); + } + } + } diff --git a/app/src/main/res/layout/activity_appblacklist.xml b/app/src/main/res/layout/activity_appblacklist.xml index 08f0445b4..d6010770f 100644 --- a/app/src/main/res/layout/activity_appblacklist.xml +++ b/app/src/main/res/layout/activity_appblacklist.xml @@ -4,11 +4,21 @@ android:layout_height="match_parent" tools:context="nodomain.freeyourgadget.gadgetbridge.activities.AppBlacklistActivity"> + + + + diff --git a/app/src/main/res/layout/item_with_checkbox.xml b/app/src/main/res/layout/item_with_checkbox.xml index 45ffc8ff2..b204546dc 100644 --- a/app/src/main/res/layout/item_with_checkbox.xml +++ b/app/src/main/res/layout/item_with_checkbox.xml @@ -47,10 +47,10 @@ + android:text="Item Description" + android:textAppearance="@style/TextAppearance.AppCompat.Small" /> \ No newline at end of file diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index f15918ae0..2365e27e8 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -5,6 +5,7 @@ Changed colours: deep sleep is now dark blue, light sleep is now light blue Support for exporting and importing of preferences in addition to the database Visual improvements of the pie charts + Add filter by name in the App blacklist activity Pebble: improve compatibility with watch app configuration pages HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) HPlus: display battery state and warn on low battery From 1a88858c6f8fa9925d9a8dd8508f240c0896c930 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 9 Apr 2017 01:09:23 +0200 Subject: [PATCH 11/40] Fix some findbugs findings --- .../appmanager/AppManagerActivity.java | 3 +- .../devices/hplus/HPlusConstants.java | 98 +++++++++---------- .../gadgetbridge/model/MusicSpec.java | 10 ++ .../gadgetbridge/model/NotificationType.java | 4 +- .../service/btle/profiles/ValueDecoder.java | 2 +- .../devices/liveview/LiveviewIoThread.java | 2 - .../devices/miband2/MiBand2Support.java | 2 +- .../operations/FetchActivityOperation.java | 2 +- .../pebble/AppMessageHandlerZalewszczak.java | 4 +- .../pebble/DatalogSessionAnalytics.java | 6 -- .../gadgetbridge/util/CheckSums.java | 7 +- .../gadgetbridge/util/FileUtils.java | 8 +- .../gadgetbridge/util/Version.java | 5 + 13 files changed, 81 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java index 618e3b662..4d16119ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java @@ -199,8 +199,7 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { static synchronized ArrayList getUuidsFromFile(String filename) { ArrayList uuids = new ArrayList<>(); - try { - FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename); + try (FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename)) { BufferedReader in = new BufferedReader(fileReader); String line; while ((line = in.readLine()) != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 2d6dc678a..a3b7f5dd2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -133,57 +133,57 @@ public final class HPlusConstants { public static final Map transliterateMap = new HashMap(){ { //These are missing - put('ó', new Byte((byte) 111)); - put('Ó', new Byte((byte) 79)); - put('í', new Byte((byte) 105)); - put('Í', new Byte((byte) 73)); - put('ú', new Byte((byte) 117)); - put('Ú', new Byte((byte) 85)); + put('ó', (byte) 111); + put('Ó', (byte) 79); + put('í', (byte) 105); + put('Í', (byte) 73); + put('ú', (byte) 117); + put('Ú', (byte) 85); //These mostly belong to the extended ASCII table - put('Ç', new Byte((byte) 128)); - put('ü', new Byte((byte) 129)); - put('é', new Byte((byte) 130)); - put('â', new Byte((byte) 131)); - put('ä', new Byte((byte) 132)); - put('à', new Byte((byte) 133)); - put('ã', new Byte((byte) 134)); - put('ç', new Byte((byte) 135)); - put('ê', new Byte((byte) 136)); - put('ë', new Byte((byte) 137)); - put('è', new Byte((byte) 138)); - put('Ï', new Byte((byte) 139)); - put('Î', new Byte((byte) 140)); - put('Ì', new Byte((byte) 141)); - put('Ã', new Byte((byte) 142)); - put('Ä', new Byte((byte) 143)); - put('É', new Byte((byte) 144)); - put('æ', new Byte((byte) 145)); - put('Æ', new Byte((byte) 146)); - put('ô', new Byte((byte) 147)); - put('ö', new Byte((byte) 148)); - put('ò', new Byte((byte) 149)); - put('û', new Byte((byte) 150)); - put('ù', new Byte((byte) 151)); - put('ÿ', new Byte((byte) 152)); - put('Ö', new Byte((byte) 153)); - put('Ü', new Byte((byte) 154)); - put('¢', new Byte((byte) 155)); - put('£', new Byte((byte) 156)); - put('¥', new Byte((byte) 157)); - put('ƒ', new Byte((byte) 159)); - put('á', new Byte((byte) 160)); - put('ñ', new Byte((byte) 164)); - put('Ñ', new Byte((byte) 165)); - put('ª', new Byte((byte) 166)); - put('º', new Byte((byte) 167)); - put('¿', new Byte((byte) 168)); - put('¬', new Byte((byte) 170)); - put('½', new Byte((byte) 171)); - put('¼', new Byte((byte) 172)); - put('¡', new Byte((byte) 173)); - put('«', new Byte((byte) 174)); - put('»', new Byte((byte) 175)); + put('Ç', (byte) 128); + put('ü', (byte) 129); + put('é', (byte) 130); + put('â', (byte) 131); + put('ä', (byte) 132); + put('à', (byte) 133); + put('ã', (byte) 134); + put('ç', (byte) 135); + put('ê', (byte) 136); + put('ë', (byte) 137); + put('Ï', (byte) 139); + put('è', (byte) 138); + put('Î', (byte) 140); + put('Ì', (byte) 141); + put('Ã', (byte) 142); + put('Ä', (byte) 143); + put('É', (byte) 144); + put('æ', (byte) 145); + put('Æ', (byte) 146); + put('ô', (byte) 147); + put('ö', (byte) 148); + put('ò', (byte) 149); + put('û', (byte) 150); + put('ù', (byte) 151); + put('ÿ', (byte) 152); + put('Ö', (byte) 153); + put('Ü', (byte) 154); + put('¢', (byte) 155); + put('£', (byte) 156); + put('¥', (byte) 157); + put('ƒ', (byte) 159); + put('á', (byte) 160); + put('ñ', (byte) 164); + put('Ñ', (byte) 165); + put('ª', (byte) 166); + put('º', (byte) 167); + put('¿', (byte) 168); + put('¬', (byte) 170); + put('½', (byte) 171); + put('¼', (byte) 172); + put('¡', (byte) 173); + put('«', (byte) 174); + put('»', (byte) 175); } }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java index 83b410cb8..7d0ff8ef7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java @@ -62,6 +62,16 @@ public class MusicSpec { this.duration == musicSpec.duration && this.trackCount == musicSpec.trackCount && this.trackNr == musicSpec.trackNr; + } + @Override + public int hashCode() { + int result = artist != null ? artist.hashCode() : 0; + result = 31 * result + (album != null ? album.hashCode() : 0); + result = 31 * result + (track != null ? track.hashCode() : 0); + result = 31 * result + duration; + result = 31 * result + trackCount; + result = 31 * result + trackNr; + return result; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index 5c3e856cf..e9246c943 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -39,8 +39,8 @@ public enum NotificationType { GENERIC_ALARM_CLOCK(PebbleIconID.ALARM_CLOCK, PebbleColor.Red); // Note: if you add any more constants, update all clients as well - public int icon; - public byte color; + public final int icon; + public final byte color; NotificationType(int icon, byte color) { this.icon = icon; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java index f093bb12c..f5fd359c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java @@ -30,7 +30,7 @@ public class ValueDecoder { int percent = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); if (percent > 100 || percent < 0) { LOG.warn("Unexpected percent value: " + percent + ": " + GattCharacteristic.toString(characteristic)); - percent = Math.max(100, Math.min(0, percent)); + percent = Math.min(100, Math.max(0, percent)); } return percent; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java index 47cab85e6..0d811aa12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java @@ -197,8 +197,6 @@ public class LiveviewIoThread extends GBDeviceIoThread { break; case HEADER_LEN: int headerSize = 0xff & incoming[0]; - if (headerSize < 0) - throw new IOException(); state = ReaderState.HEADER; incoming = new byte[headerSize]; break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index 2f849a432..41f6311e8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -810,7 +810,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } private void handleButtonPressed(byte[] value) { - LOG.info("Button pressed: " + value); + LOG.info("Button pressed"); logMessageContent(value); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index 5260a28cc..160ce8cf1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -216,7 +216,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { int len = value.length; if (len % 4 != 1) { - throw new AssertionError("Unexpected activity array size: " + value); + throw new AssertionError("Unexpected activity array size: " + len); } for (int i = 1; i < len; i+=4) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java index 9c8e41031..399686c49 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java @@ -69,8 +69,8 @@ class AppMessageHandlerZalewszczak extends AppMessageHandler { } ArrayList> pairs = new ArrayList<>(2); - pairs.add(new Pair<>(KEY_TEMP, (Object) (Math.round(weatherSpec.currentTemp - 273) + "C"))); - pairs.add(new Pair<>(KEY_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode)))); + pairs.add(new Pair(KEY_TEMP, weatherSpec.currentTemp - 273 + "C")); + pairs.add(new Pair(KEY_ICON, getIconForConditionCode(weatherSpec.currentConditionCode))); byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs); ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java index 80659c5dc..4fbb31116 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java @@ -31,15 +31,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; class DatalogSessionAnalytics extends DatalogSession { private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionAnalytics.class); private GBDeviceEventBatteryInfo mGBDeviceEventBatteryInfo = new GBDeviceEventBatteryInfo(); - private GBDevice mGBDevice; DatalogSessionAnalytics(byte id, UUID uuid, int timestamp, int tag, byte itemType, short itemSize, GBDevice device) { super(id, uuid, timestamp, tag, itemType, itemSize); - if (mGBDevice == null || !device.equals(mGBDevice)) { //prevent showing information of other pebble watches when switching devices - mGBDevice = device; - mGBDeviceEventBatteryInfo.state = BatteryState.UNKNOWN; - } - // The default notification should not be too bad (one per hour) but we can override this if needed //mGBDevice.setBatteryThresholdPercent((short) 5); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java index e7679301f..e47e023fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java @@ -71,16 +71,17 @@ public class CheckSums { } } - public static byte[] readAll(InputStream in, long maxLen) throws IOException { + // copy&paste of FileUtils.readAll() to have it free from Android dependencies + private static byte[] readAll(InputStream in, long maxLen) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(8192, in.available())); byte[] buf = new byte[8192]; - int read = 0; + int read; long totalRead = 0; while ((read = in.read(buf)) > 0) { out.write(buf, 0, read); totalRead += read; if (totalRead > maxLen) { - throw new IOException("Too much data to read into memory. Got already " + totalRead + buf); + throw new IOException("Too much data to read into memory. Got already " + totalRead); } } return out.toByteArray(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index b01198a96..ba6150f01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -54,7 +54,9 @@ public class FileUtils { if (!sourceFile.exists()) { throw new IOException("Does not exist: " + sourceFile.getAbsolutePath()); } - copyFile(new FileInputStream(sourceFile), new FileOutputStream(destFile)); + try (FileInputStream in = new FileInputStream(sourceFile); FileOutputStream out = new FileOutputStream(destFile)) { + copyFile(in, out); + } } private static void copyFile(FileInputStream sourceStream, FileOutputStream destStream) throws IOException { @@ -229,13 +231,13 @@ public class FileUtils { public static byte[] readAll(InputStream in, long maxLen) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(8192, in.available())); byte[] buf = new byte[8192]; - int read = 0; + int read; long totalRead = 0; while ((read = in.read(buf)) > 0) { out.write(buf, 0, read); totalRead += read; if (totalRead > maxLen) { - throw new IOException("Too much data to read into memory. Got already " + totalRead + buf); + throw new IOException("Too much data to read into memory. Got already " + totalRead); } } return out.toByteArray(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java index c1db22ec3..df0efe2c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java @@ -61,4 +61,9 @@ public class Version implements Comparable { return false; return this.compareTo((Version) that) == 0; } + + @Override + public int hashCode() { + return version.hashCode(); + } } \ No newline at end of file From fe626eb11e448a0539f67fea5bdbad7db9d26fb0 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 9 Apr 2017 16:01:48 +0200 Subject: [PATCH 12/40] Remove the checkboxes in the alarms cardview and simplify layout of details activity - rename the layout file of the alarm item to better organize the files - add a color selector for the item view, this replicates the old behavior of using the color to highlight enabled days - remove the nested linearlayouts in the alarm details activity layout and use CheckedTextView instead --- .../gadgetbridge/activities/AlarmDetails.java | 80 ++++-- .../adapter/GBAlarmListAdapter.java | 2 +- app/src/main/res/color/alarm_dow.xml | 5 + .../res/layout/activity_alarm_details.xml | 236 +++++------------- .../layout/{alarm_item.xml => item_alarm.xml} | 29 +-- 5 files changed, 145 insertions(+), 207 deletions(-) create mode 100644 app/src/main/res/color/alarm_dow.xml rename app/src/main/res/layout/{alarm_item.xml => item_alarm.xml} (85%) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index d14db9dc6..543673de3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -21,8 +21,7 @@ import android.os.Bundle; import android.text.format.DateFormat; import android.view.MenuItem; import android.view.View; -import android.widget.CheckBox; -import android.widget.TextView; +import android.widget.CheckedTextView; import android.widget.TimePicker; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -36,16 +35,15 @@ public class AlarmDetails extends GBActivity { private GBAlarm alarm; private TimePicker timePicker; - private CheckBox cbSmartWakeup; - private CheckBox cbMonday; - private CheckBox cbTuesday; - private CheckBox cbWednesday; - private CheckBox cbThursday; - private CheckBox cbFriday; - private CheckBox cbSaturday; - private CheckBox cbSunday; + private CheckedTextView cbSmartWakeup; + private CheckedTextView cbMonday; + private CheckedTextView cbTuesday; + private CheckedTextView cbWednesday; + private CheckedTextView cbThursday; + private CheckedTextView cbFriday; + private CheckedTextView cbSaturday; + private CheckedTextView cbSunday; private GBDevice device; - private TextView smartAlarmLabel; @Override protected void onCreate(Bundle savedInstanceState) { @@ -56,15 +54,56 @@ public class AlarmDetails extends GBActivity { device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE); timePicker = (TimePicker) findViewById(R.id.alarm_time_picker); - smartAlarmLabel = (TextView) findViewById(R.id.alarm_label_smart_wakeup); - cbSmartWakeup = (CheckBox) findViewById(R.id.alarm_cb_smart_wakeup); - cbMonday = (CheckBox) findViewById(R.id.alarm_cb_mon); - cbTuesday = (CheckBox) findViewById(R.id.alarm_cb_tue); - cbWednesday = (CheckBox) findViewById(R.id.alarm_cb_wed); - cbThursday = (CheckBox) findViewById(R.id.alarm_cb_thu); - cbFriday = (CheckBox) findViewById(R.id.alarm_cb_fri); - cbSaturday = (CheckBox) findViewById(R.id.alarm_cb_sat); - cbSunday = (CheckBox) findViewById(R.id.alarm_cb_sun); + cbSmartWakeup = (CheckedTextView) findViewById(R.id.alarm_cb_smart_wakeup); + cbMonday = (CheckedTextView) findViewById(R.id.alarm_cb_monday); + cbTuesday = (CheckedTextView) findViewById(R.id.alarm_cb_tuesday); + cbWednesday = (CheckedTextView) findViewById(R.id.alarm_cb_wednesday); + cbThursday = (CheckedTextView) findViewById(R.id.alarm_cb_thursday); + cbFriday = (CheckedTextView) findViewById(R.id.alarm_cb_friday); + cbSaturday = (CheckedTextView) findViewById(R.id.alarm_cb_saturday); + cbSunday = (CheckedTextView) findViewById(R.id.alarm_cb_sunday); + + + cbSmartWakeup.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbMonday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbTuesday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbWednesday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbThursday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbFriday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbSaturday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + cbSunday.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); timePicker.setIs24HourView(DateFormat.is24HourFormat(GBApplication.getContext())); timePicker.setCurrentHour(alarm.getHour()); @@ -73,7 +112,6 @@ public class AlarmDetails extends GBActivity { cbSmartWakeup.setChecked(alarm.isSmartWakeup()); int smartAlarmVisibility = supportsSmartWakeup() ? View.VISIBLE : View.GONE; cbSmartWakeup.setVisibility(smartAlarmVisibility); - smartAlarmLabel.setVisibility(smartAlarmVisibility); cbMonday.setChecked(alarm.getRepetition(GBAlarm.ALARM_MON)); cbTuesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_TUE)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java index a63606d14..92292aca3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java @@ -93,7 +93,7 @@ public class GBAlarmListAdapter extends RecyclerView.Adapter + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_alarm_details.xml b/app/src/main/res/layout/activity_alarm_details.xml index 5cc1b4703..276e8d003 100644 --- a/app/src/main/res/layout/activity_alarm_details.xml +++ b/app/src/main/res/layout/activity_alarm_details.xml @@ -9,32 +9,21 @@ android:paddingTop="@dimen/activity_vertical_margin" tools:context="nodomain.freeyourgadget.gadgetbridge.activities.AlarmDetails"> - - - - - - - + android:layout_marginStart="4dp" + android:drawableStart="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_smart_wakeup" + android:textAppearance="?android:attr/textAppearanceSmall" /> + android:layout_marginBottom="20dp" /> - - - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_mon_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_tue_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_wed_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_thu_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_fri_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> - - - - - - - - - - - - - + android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:gravity="center" + android:text="@string/alarm_sat_short" + android:textAppearance="?android:attr/textAppearanceSmall" /> + - diff --git a/app/src/main/res/layout/alarm_item.xml b/app/src/main/res/layout/item_alarm.xml similarity index 85% rename from app/src/main/res/layout/alarm_item.xml rename to app/src/main/res/layout/item_alarm.xml index 470abf959..875b5f469 100644 --- a/app/src/main/res/layout/alarm_item.xml +++ b/app/src/main/res/layout/item_alarm.xml @@ -25,7 +25,10 @@ android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_alignParentTop="true" - android:layout_margin="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + android:layout_marginEnd="0dp" android:text="00:00" android:textAppearance="?android:attr/textAppearanceLarge" /> @@ -43,7 +46,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" - android:layout_alignParentTop="true" /> + android:layout_alignParentTop="true" + android:layout_margin="8dp" /> @@ -71,8 +74,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_tue_short" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -83,8 +85,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_wed_short" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -95,8 +96,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_thu_short" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -107,8 +107,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_fri_short" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -119,8 +118,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_sat_short" android:textAppearance="?android:attr/textAppearanceSmall" /> @@ -131,8 +129,7 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_weight="1" - android:checked="false" - android:drawableTop="?android:attr/listChoiceIndicatorMultiple" + android:textColor="@color/alarm_dow" android:gravity="center" android:text="@string/alarm_sun_short" android:textAppearance="?android:attr/textAppearanceSmall" /> From 5bf6251dc53a508f06fc74814b1088197255b8bd Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 9 Apr 2017 16:03:07 +0200 Subject: [PATCH 13/40] Rename some layout files as they are used for specific purposes --- .../gadgetbridge/activities/AppBlacklistActivity.java | 2 +- .../activities/appmanager/AbstractAppManagerFragment.java | 2 +- .../layout/{item_with_checkbox.xml => item_app_blacklist.xml} | 0 ...ith_details_and_drag_handle.xml => item_pebble_watchapp.xml} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename app/src/main/res/layout/{item_with_checkbox.xml => item_app_blacklist.xml} (100%) rename app/src/main/res/layout/{item_with_details_and_drag_handle.xml => item_pebble_watchapp.xml} (100%) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index b83956cd6..cb975442a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -64,7 +64,7 @@ public class AppBlacklistActivity extends GBActivity { RecyclerView appListView = (RecyclerView) findViewById(R.id.appListView); appListView.setLayoutManager(new LinearLayoutManager(this)); - appBlacklistAdapter = new AppBlacklistAdapter(R.layout.item_with_checkbox, this); + appBlacklistAdapter = new AppBlacklistAdapter(R.layout.item_app_blacklist, this); appListView.setAdapter(appBlacklistAdapter); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index e2ad884f6..90008f0cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -276,7 +276,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { } }); appListView.setLayoutManager(new LinearLayoutManager(getActivity())); - mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_with_details_and_drag_handle, this); + mGBDeviceAppAdapter = new GBDeviceAppAdapter(appList, R.layout.item_pebble_watchapp, this); appListView.setAdapter(mGBDeviceAppAdapter); ItemTouchHelper.Callback appItemTouchHelperCallback = new AppItemTouchHelperCallback(mGBDeviceAppAdapter); diff --git a/app/src/main/res/layout/item_with_checkbox.xml b/app/src/main/res/layout/item_app_blacklist.xml similarity index 100% rename from app/src/main/res/layout/item_with_checkbox.xml rename to app/src/main/res/layout/item_app_blacklist.xml diff --git a/app/src/main/res/layout/item_with_details_and_drag_handle.xml b/app/src/main/res/layout/item_pebble_watchapp.xml similarity index 100% rename from app/src/main/res/layout/item_with_details_and_drag_handle.xml rename to app/src/main/res/layout/item_pebble_watchapp.xml From 810ba5419be080170c351936cd72a0e368a10d17 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 9 Apr 2017 17:05:09 +0200 Subject: [PATCH 14/40] Pebble: reenable battery level reporting, with percentage drom datalog --- .../pebble/DatalogSessionAnalytics.java | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java index 4fbb31116..2047fa313 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java @@ -50,11 +50,13 @@ class DatalogSessionAnalytics extends DatalogSession { datalogMessage.position(datalogMessage.position() + 12); short reportedMilliVolts = datalogMessage.getShort(); - LOG.info("Battery reading for TS " + messageTS + " is: " + reportedMilliVolts + " milliVolts, mapped to percentage: " + milliVoltstoPercentage(reportedMilliVolts)); + datalogMessage.position(datalogMessage.position() + 2); + byte reportedPercentage = datalogMessage.get(); - if (false && (messageTS > 0 && reportedMilliVolts < 5000)) { //some safety checks, always disabled for now + LOG.info("Battery reading for TS " + messageTS + " is: " + reportedMilliVolts + " milliVolts, percentage: " + reportedPercentage); + if (messageTS > 0 && reportedMilliVolts < 5000) { //some safety checks mGBDeviceEventBatteryInfo.state = BatteryState.BATTERY_NORMAL; - mGBDeviceEventBatteryInfo.level = milliVoltstoPercentage(reportedMilliVolts); + mGBDeviceEventBatteryInfo.level = reportedPercentage; return new GBDeviceEvent[]{mGBDeviceEventBatteryInfo, null}; } else { //invalid data, but we ack nevertheless @@ -62,30 +64,4 @@ class DatalogSessionAnalytics extends DatalogSession { } } - - private short milliVoltstoPercentage(short batteryMilliVolts) { - if (batteryMilliVolts > 4145) { //(4146 is still 100, next reported value is already 90) - return 100; - } else if (batteryMilliVolts > 4053) { //(4054 is still 90, next reported value is already 80) - return 90; - } else if (batteryMilliVolts > 4000) { //guessed - return 80; - } else if (batteryMilliVolts > 3890) { //3890 was already 60 - return 70; - } else if (batteryMilliVolts > 3855) { //probably - return 60; - } else if (batteryMilliVolts > 3780) { //3781 is still 50, next reading is 3776 but percentage on pebble unknown - return 50; - } else if (batteryMilliVolts >= 3750) { //3750 is still 40, next reported value is 3746 and already 30 - return 40; - } else if (batteryMilliVolts > 3720) { //3723 is still 30, next reported value is 3719 and already 20 - return 30; - } else if (batteryMilliVolts > 3680) { //3683 is still 20, next reported value is 3675 and already 10 - return 20; - } else if (batteryMilliVolts > 3650) { //3657 is still 10 - return 10; - } else { - return 0; //or -1 for invalid? - } - } } From a5fdc90b6eaec8607428561ac12da4f5d5bbb28a Mon Sep 17 00:00:00 2001 From: Translation Bot Date: Sun, 9 Apr 2017 22:35:46 +0200 Subject: [PATCH 15/40] update translations from transifex, thanks! --- app/src/main/res/values-cs/strings.xml | 34 +++++ app/src/main/res/values-de/strings.xml | 4 + app/src/main/res/values-es/strings.xml | 17 ++- app/src/main/res/values-fr/strings.xml | 17 ++- app/src/main/res/values-it/strings.xml | 11 ++ app/src/main/res/values-ja/strings.xml | 18 +++ app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-pt/strings.xml | 162 +++++++++++++++++++-- 8 files changed, 247 insertions(+), 18 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 3a75afb43..7ea09ae6b 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1,6 +1,9 @@ + Gadgetbridge + Gadgetbridge Nastavení + Ladění Ukončit Načíst data Monitor spánku (alfa) @@ -10,6 +13,13 @@ Odstranit zařízení Odstranit %1$s Odstraní zařízení a vymaže všechna data! + Otevřít navigační lištu + Zavřít navigační lištu + Podržte kartu déle pro odpojení + Odpojuji + Připojuji + Sejmout snímek obrazovky + Ladění Manažer aplikací Aplikace v cache @@ -53,9 +63,12 @@ Tmavé Jazyk Skrýt notifikace Gadgetbridge + Ikona ve stavové liště a notifikace na zamčeném displeji budou zobrazeny + Ikona ve stavové liště a notifikace na zamčeném displeji nebudou zobrazeny Notifikace Opakování Volání + SMS Zpráva Pebble Podpora pro aplikace, které posílají notifikace do Pebble via PebbleKit. Obecná podpora notifikací @@ -112,6 +125,7 @@ Povolí funkce, které nebyl testovány. POVOLTE, JEN POKUD VÍTE, CO DĚLÁTE! Vždy preferovat BLE Použít experimentální podporu Pebble LE pro všechny Pebble místo BT classic, vyžaduje spárování \"Pebble LE\" po připojení bez LE + Pebble 2/LE GATT MTU limit Pokud vaše Pebble 2/Pebble LE nepracuje jak má, zkuste toto nastavení pro omezení MTU (povolený rozsah 20-512) Zapnout logování Watch App Logy od watch app budou logovány v Gadgetbridge (vyžaduje znovupřipojení) @@ -127,7 +141,10 @@ připojování připojeno neznámý stav + HW: %1$s FW: %2$s + FW: %1$s (neznámé) + Test Test notifikací Toto je notifikace z Gadgetbridge BT není podporován. @@ -150,6 +167,7 @@ Zastavit hledání Spustit hledání Připojit nové zařízení + %1$s (%2$s) Párovat zařízení Použijte párování BT Androidu pro spárování se zařízením. Párovat Mi Band @@ -169,6 +187,7 @@ Data uživatele nejsou platná, nyní používám vzorová. Když Mi Band zavibruje a blikne, dotkněte se jej několikrát po sobě. Instalovat + Nastavte své zařízení pro vyhledání. Již připojená zařízení nebudou vyhledána. Zapněte lokalizaci (GPS) pro mobily s Androidem 6 a vyšším. Vypněte hlídání soukromí pro Gadgetbridge, protože může způsobit nestabilitu mobilu. Pokud není zařízení vyhledáno během několika minut, zkuste to znovu po restartu mobilu. Poznámka: Obraz zařízení Jméno/přezdívka @@ -180,6 +199,7 @@ Z %1$s do %2$s Nosíte vlevo nebo vpravo? Profil vibrací + Staccato Krátké Střední Dlouhé @@ -228,15 +248,19 @@ Počet nabití: %s Váš spánek Spánek za týden + Spánek dnes, cíl: %1$s Kroky za týden Vaše aktivita a spánek Nahrávám firmware... Soubor nelze nainstalovat, zařízení není připraveno. + Mi Band Firmware %1$s Kompatibilní verze Netestovaná verze! Připojení k zařízení: %1$s + Pebble Firmware %1$s Správná revize HW Revize HW není správná! + %1$s (%2$s) Při přenosu firmwaru nastaly potíže. Nerestartujte svůj Mi Band! Problém při přenosu matadat firmware Instalace firmware je kompletní @@ -269,6 +293,7 @@ Časový posun zařízení v hodinách (pro zjišťování spánku směnařů) Mi2: formát data Čas + Zapnout displej při zvednutí Přenáším data od %1$s čekání na znovupřipojení @@ -279,10 +304,14 @@ Váha v kg ověřování ověřování vyžadováno + Spí... Přidat widget Preferovaná doba spánku v hodinách Budík nastaven na %1$02d:%2$02d + HW: %1$s + FW: %1$s Chyba při vytváření adresáře pro logy: %1$s + Tep: Probíhá aktualizace firmware Firmware neodeslán Srdeční tep @@ -321,5 +350,10 @@ Toto vypněte v případě problémů s připojením Metrické Imperiální + 24h + dop./odp. Budík + (%1$s) + Nalezeno! + Formát času Mi2 diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 72c4a6596..4d29e3372 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -84,6 +84,7 @@ Privatsphäre-Modus für Anrufe Zeige Name und Telefonnumer Verstecke den Namen aber zeige die Telefonnummer an + Zeige den Namen an aber verstecke die Telefonnummer Verstecke Name und Telefonnummer App-Benachrichtigungen blockieren Vorgefertigte Nachrichten @@ -356,4 +357,7 @@ (%1$s) Gefunden! Mi2: Uhrzeit-Format + Installiere Version %1$s vor dem Installieren der Firmware! + Text Benachrichtigung + aus diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ee79d4963..eb119045e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -268,6 +268,8 @@ Instalación del firmware completa, reiniciando dispositivo... Falló la escritura del firmware Pasos + Calorías + Distancia Actividad Pasos hoy, objetivo: %1$s No confirmar transferencia @@ -325,11 +327,13 @@ Borrar la base de datos antigua No se puede acceder a la ruta para exportar . Por favor, contacta con los desarrolladores. Exportado a: %1$s - Error exportando DB: %1$s + Error de exportación de la base de datos: %1$s + Error de exportación de las preferencias: %1$s ¿Importar Datos? ¿Quiere sobreescribir la base de datos actual? Todos sus datos actuales (si los hay) se borrarán. Importado con éxito. - Error importando DB: %1$s + Error de importación de la base de datos: %1$s + Error de importación de las preferencias: %1$s ¿Quieres borrar los datos de actividad? ¿Quieres borrar la base de datos? Todos tus datos de actividad y la información sobre tus dispositivos se borrarán. Datos borrados. @@ -361,4 +365,13 @@ Notificaciones textuales = 1.0.1.28 and Mili_pro.ft* installed.]]> off + Intento de emparejamiento con %1$s + El enlace con %1$s falló instantáneamente + Intentando conectar con: %1$s + Activa el Bluetooth para encontrar dispositivos. + Correctamente conectado con %1$s + Emparejar con %1$s? + Selecciona Emparejar para emparejar tus dispositivos. Si esto falla, prueba de nuevo sin emparejar. + Emparejar + No emparejar diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 94bfb91c7..7b1e6b2d0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -268,6 +268,8 @@ Installation complète du micrologiciel, redémarrage de l\'appareil Échec lors de l\'écriture du micrologiciel Pas + Calories + Distance Activité en direct Nombre de pas aujourd\'hui, objectif: %1$s Ne pas confirmer le transfert de données d\'activités @@ -327,11 +329,13 @@ NOTE: la base de données sera bien évidement plus grande ! Effacer l\'ancienne base de données Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs. Exporter vers : %1$s - Erreur d\'exportation BD: %1$s + Erreur d\'exportation de la base de données: %1$s + Erreur d\'exportation des préférences: %1$s Importer des données ? Voulez-vous vraiment effacer la base de données actuelle ? Toutes vos données (si vous en avez) seront perdues. Importation réussie. - Erreur lors de l\'importation BD: %1$s + Erreur lors de l\'importation de la base de données: %1$s + Erreur d\'importation des préférences: %1$s Détruire les anciennes données ? Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données d\'activité et vos informations issues de vos appareils seront perdues. Les données ont été effacées. @@ -363,4 +367,13 @@ NOTE: la base de données sera bien évidement plus grande ! Notifications textuelles = 1.0.1.28 and Mili_pro.ft* installed.]]> off + Tentative d\'appairage avec %1$s + Le lien avec %1$s a échoué instantanément + Tentative de connexion à: %1$s + Activez le Bluetooth pour trouver des dispositifs + Correctement lié à %1$s + Appairer avec %1$s + Sélectionnez Appairer pour appairer vos dispositifs. Si cela échoue, essayez à nouveau sans appairage. + Appairage + Ne pas appairer diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6367a6d15..27e8a4fc7 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -13,8 +13,12 @@ Rimuovi dispositivo Rimuovi %1$s Il dispositivo verrà rimosso e tutti i dati ad esso associati verranno cancellati! + Apri menu + Chiudi menu + Pressione prolungata sulla scheda per scollegare Disconnessione Connessione + Screenshot del dispositivo Debug Gestione app @@ -76,9 +80,11 @@ sempre se lo schermo è spento mai + Privacy Impostazioni privacy chiamate Mostra nome e numero chiamante Nascondi il nome ma mostra il numero del chiamante + Nascondi il numero ma mostra il nome Nascondi nome e numero del chiamante Blocca applicazioni Messaggi preimpostati @@ -344,7 +350,12 @@ Disattiva se hai problemi di connession Metrico Imperiale + 24H + AM/PM Sveglia Trovato! Mi2: Formato dell\'orario + E\' necessario installare la verione %1$s prima di installare questo firmware! + Notifiche + spento diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ddf2c425f..64b80311f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -84,6 +84,7 @@ 通話プライバシーモード 名前と番号を表示 名前は非表示、番号は表示 + 番号は非表示、名前は表示 名前と番号を非表示 アップのブラックリスト 定型のメッセージ @@ -267,6 +268,8 @@ ファームウェアのインストールが完了しました。デバイスを再起動します… ファームウェアの書き込みに失敗しました 歩数 + カロリー + 距離 生活活動 今日の歩数、目標: %1$s 活動データ転送に応答しない @@ -325,10 +328,12 @@ エクスポート パスにアクセスできません。開発者にご連絡ください。 エクスポートしました: %1$s DB のエクスポート時にエラー: %1$s + 設定のエクスポート時にエラー: %1$s データをインポートしますか? 現在のデータベースを上書きしてもよろしいですか? 現在の活動データは (もしあれば) すべて失われます。 インポートに成功しました。 DB のインポート時にエラー: %1$s + 設定のインポート時にエラー: %1$s 活動データを削除しますか? データベース全体を削除してもよろしいですか? 活動データとお使いのデバイスに関する情報がすべて失われます。 データを正常に削除しました。 @@ -356,4 +361,17 @@ (%1$s) 見つかりました! Mi2: 時刻形式 + このファームウェアをインストールする前に、バージョン %1$s をインストールする必要があります! + テキスト通知 + = 1.0.1.28 と Mili_pro.ft* をインストールしていることが必要です。]]> + オフ + %1$s とペアを試みています + %1$s との接続がすぐに失敗しました。 + 接続の試行中: %1$s + デバイスを見つけるため Bluetooth を有効にします。 + %1$s と接続しました。 + %1$sとペアにしますか? + お使いのデバイスとペアにする相手を選択します。 これに失敗した場合は、ペア設定をせずに再試行してください。 + ペア + ペアにしない diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 942572bdf..2e451f1cf 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -289,7 +289,7 @@ Sobre para transferir dados desde %1$s aguarde para reconectar Sobre você - Ano de aniversário + Ano de anoversário Gênero Altura em cm Peso em kg diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b773377ff..42014176f 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,5 +1,7 @@ + Gadgetbridge + Gadgetbridge Configurações Depurar Sair @@ -11,6 +13,12 @@ Apagar dispositivo Apagar %1$s Isto irá apagar o dispositivo e apagar os dados associados! + Abrir gaveta de navegação + Fechar gaveta de navegação + Pressione o cartão longamente para desligar + A desligar + A ligar + Capturar o ecrã do dispositivo Depurar Administrador de App @@ -20,12 +28,12 @@ Apagar Apagar e remover do cache Reinstalar - Procurar na loja Pebble + Buscar na loja Pebble Ativar Desativar Ativar HRM Desativar HRM - Ativar app de clima do sistema + Ativas app de clima do sistema Desativar app de clima do sistema Instalar notificações do app de clima Configurar @@ -35,7 +43,10 @@ Instalador FW/App Está prestes a instalar o firmware %s no lugar do atual na sua Mi Band. + Estás prestes a instalar os firmwares %1$s e %2$s em vez dos que estão atualmente na sua Mi Band. O firmware foi testado e é compatível com o Gadgetbridge. + Este firmware não foi testado e pode não ser compatível com o Gadgetbridge.\n\nÉ preferível que NÃO o instale na sua Mi Band! + Se ainda assim quiser continuar e tudo continuar a funcionar normalmente, por favor diga aos programadores do Gadgetbridge para permitir o firmware versão: %s Configurações Configurações Gerais @@ -46,6 +57,7 @@ Padrão Data e Hora Sincronizar hora + Sincronizar a hora para o dispositivo a quando da ligação e quando a hora ou fuso horário se alterarem no Android Tema Claro Escuro @@ -61,8 +73,10 @@ Suportar notificações de aplicações que enviam notificações pelo PebbleKit. Suportar notificações genéricas ... e quando o ecrã estiver ligado - Não perturbe - Ignorar notificações indesejadas enquanto estiver no modo Não Perturbe. + Não incomodar + Ignorar notificações indesejadas enquanto estiver no modo Não Incomodar. + Transliteração + Ativar apenas se o seu dispositivo não suportar a sua caracteres da sua língua (Atualmente apenas Cirílico) sempre quando o ecrã estiver desligado nunca @@ -70,6 +84,7 @@ Modo de chamada privada Exibir nome e número Ocultar nome e exibir número + Ocular o número e exibir o nome Ocultar nome e número Aplicações ignoradas Histórico de mensagens @@ -85,14 +100,17 @@ Sincronizar com Pebble Health Sincronizar com Misfit Sincronizar com Morpheuz + Suportar chamadas efetuadas + Desactivar isto também irá fazer com que o Pebble 2/LE não vibre durante as chamadas efetuadas + Permitir acesso por Apps Android de terceiros. Ativar suporte experimental a Apps Android que usem PebbleKit Despertar e pôr do sol - Enviar despertar e pôr do sol com base na localização do pebble + Enviar despertar e pôr do sol baseado na localização do Pebble Auto remover notificações rejeitadas Notificações são automaticamente removidas quando rejeitadas no Android Modo de privacidade Notificações - Deslocar texto de notificações que extravasar o ecrã + Deslocar texto de notificações que extravasar o ecrã Apenas mostrar ícone de notificações Localização Obter localização @@ -106,42 +124,88 @@ Esta opção força o uso do protocolo de notificação mais recente. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER! Permitir recursos não certificados Permitir recursos não certificados. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER! - Preferir sempre BLE + Sempre preferir BLE + Utilizar suporte para Pebble LE em todos os Pebbles em vez do clássico BT. Necessita que se emparelhe um \"Pebble LE\" depois de dispositivos não LE se terem ligado uma vez + Pebble 2/LE GATT MTU limite + Se o seu Pebble2/Pebble LE não funciona como esperado, ative esta opção para limitar o tamanho das transferências (intervalo válido é 20-512) + Activar Registo da Aplicação do Dispositivo + Causa que os registos das aplicações do relógio sejam guardados pelo Gadgetbridge (necessita de religação) + Antecipar confirmações do PebbleKit + Irá fazer com que as mensagens enviadas para aplicações de terceiros sejam sempre imediatamente confirmadas + Tentativas de Ligação Unidades Formato da hora Duração do ecrã + Medição Contínua de Ritmo Cardíaco + Configurações HPlus/Makibes desligado a ligar ligado estado desconhecido + HW: %1$s FW: %2$s + FW: %1$s + (desconhecido) Teste - Teste de notificações + Teste de notificação + Isto é uma notificação de teste do Gadgetbridge Bluetooth não suportado Bluetooth desligado + Toque num dispositivo ligado para Gerir Aplicações + Toque num dispositivo ligado para ver a Atividade + Toque num dispositivo ligado para o fazer Vibrar + Toque num dispositivo para ligar + Não foi possível ligar. Endereço Bluetooth inválido? + Gadgetbridge a executar + A instalar o binário %1$d/%2$d falha na instalação! instalação bem sucedida + ESTÁ A TENTAR INSTALAR UM FIRMWARE, CONTINUE À SUA RESPONSABILIDADE.\n\n\n Este firmware é para a Versão de HW: %s + Está prestes a instalar a aplicação seguinte::\n\n\n%1$s Versão %2$s por %3$s\n + N/D inicializado - Dispositivo encontrado + %1$s por %2$s + Procurar Dispositivos Terminar a procura Iniciar a procura Ligue o novo dispositivo + %1$s (%2$s) Emparelhar dispositivo + Use o diálogo do sistema Android para emparelhar com o dispositivo + Emparelhar com a sua Mi Band A emparelhar com %s... + A criar ligação com %1$s (%2$s) + Não foi possível emparelhar com %1$s (%2$s) + Associação em progresso: %1$s (%2$s) + Já associado com %1$s (%2$s), a ligar... + Nenhum endereço MAC fornecido, não é possível emparelhar. + Configurações Específicas do Dispositivo + Configurações Mi Band masculino feminino outro esquerda direita + Sem dados de utilizador. Por enquanto serão usados dados fictícios + Quando a sia Mi Band vibrar e piscar, dê-lhe alguns toques seguidos. Instalar + Torne o seu dispositivo visível. É improvável que os dispositivos atualmente ligados sejam apresentados. Ative a localização (i.e, GPS) no Android 6+. Desative também a Proteção de Privacidade para o Gadgetbridge, pois esta poderá bloquear e levar ao reinício do seu telefone. Se depois de alguns minutos não for encontrado qualquer dispositivo, tente outra vez após reiniciar o telefone. Nota: + Imagem do Dispositivo Nome/Apelido Quantidade de vibrações Monitor de sono Escrever arquivos de registo Inicializando + A Descarregar Dados de Atividade + De %1$s até %2$s + De que lado a usa? + Perfil de Vibração + Destacado Pequeno Médio Longo + Gota de Água + Toque Alarme Vibração Tente @@ -153,6 +217,8 @@ Conversas Navegação Rede social + Encontrar dispositivo perdido + Cancele para parar a vibração. A sua atividade Configurar Alarmes Configurar alarmes @@ -168,28 +234,52 @@ Existiu um erro ao definir o alarme, tente novamente! Alarme enviado para o dispositivo! Sem data. Sincronizar com o dispositivo? + Prestes a transferir %1$s de dados desde %2$s Objetivo de passos por dia + Erro ao executar \'%1$s\' A Sua Atividade (ALPHA) + Não foi possível ligar: %1$s Não foi possível encontrar um manipulador para instalar o arquivo. + Não foi possível instalar o ficheiro: %1$s + Não foi possível instalar o firmware fornecido: ele não corresponde à versão de hardware do Pebble + Aguarde enquanto se determina o estado da instalação... Dispositivo com bateria baixa! + %1$s bateria restante: %2$s%% + Última carga: %s \n + Número de cargas: %s O Seu Sono + Sono durante a semana + Sono hoje, objetivo: %1$s Passos na semana A Sua Atividade e Sono Atualizando Firmware... Arquivo não pode ser instalado, o dispositivo não está pronto. + Firmware da Mi Band %1$s Versão compatível Versão não testada! + Ligação do Dispositivo: %1$s + Firmware da Pebble: %1$s + Revisão de hardware correta + Revisão de hardware inválida! + %1$s (%2$s) + Ocorreu um erro com a transferência do firmware. NÃO REINICIE a sua Mi Band! Problemas ao transferir os metadados do firmware Instalação do Firmware completa Instalação do Firmware completa, reiniciando o dispositivo... Falha ao instalar o Firmware Passos - Atividade em Tempo Real + Atividade em tempo real + Passos hoje, objetivo: %1$s + Não confirmar a transferência de dados de atividade + Se os dados de atividade não forem confirmados pela banda, eles não serão apagados. Útil se o GB é utilizado em simultâneo com outras aplicações. + Irá manter os dados na Mi Band mesmo após a sincronização. Útil se o GB é utilizado em simultâneo com outras aplicações. + Usar modo de baixa latência para atualizações de FW + Isto pode ajudar em dispositivos onde as atualizações de firmware falhem Histórico de passos Passos/min atuais Total de passos Histórico de passos por minuto - Iniciar a sua atividade + Iniciar sua atividade Atividade Sono leve Sono profundo @@ -200,7 +290,13 @@ Firmware incompatível Este firmware não é compatível com seu dispositivo Alarmes reservados para eventos próximos + Utilizar sensor de Ritmo Cardíaco para melhorar a deteção de sono + Compensação da hora do dispositivo em horas (para detetar o sono de trabalhadores por turnos) + Mi2: Formato da Data Hora + + Ativar ecrã do dispositivo quando o levantar + Prestes a transferir dados desde %1$s aguarde para tornar a ligar Sobre você Ano de nascimento @@ -209,12 +305,39 @@ Peso em kg a autenticar autenticação necessária + Zzz Adicionar widget - Preferir duração do sono em horas + Preferir duração de sono em horas + Alarme definido para as %1$02d:%2$02d + HW: %1$s + FW: %1$s + Erro ao criar o diretório para os ficheiros de registo: %1$s + RH: Atualização de Firmware em curso Firmware não enviado + Ritmo Cardíaco + Ritmo Cardíaco + Armazenar registo não processado na base de dados. + Se ativado os dados são guardados no seu formato original, estando mais tarde disponíveis para interpretação. Nota: neste caso a base de dados será maior! + Gestão da Base de Dados + Gestão da Base de Dados + As operações sobre a base de dados utilizam o seguinte caminho no seu dispositivo. \nEste caminho está acessível a outras aplicações Android e ao seu computador. \nEspere encontrar a sua base de dados exportada (ou a base de dados que pretende importar) nessa localização: + Apagar Base de Dados Antiga + Não foi possível aceder ao caminho de exportação. Por favor contacte os programadores. + Exportado para: %1$s + Erro ao exportar a Base de Dados: %1$s Importar dados? - Dados importados com sucesso. + Realmente deseja sobrescrever a base de dados atual? Todos os dados de atividade existentes irão perder-se. + Importação com sucesso. + Erro ao importar a Base de Dados: %1$s + Apagar os dados de atividade? + Apagar realmente toda a base de dados? Todos os seus dados de atividade e informação sobre os seus dispositivos irá perder-se. + Dados apagados com sucesso. + Não foi possível apagar a base de dados. + Apagar os dados de atividade antigos? + Apagar realmente a base de dados antiga? Dados de atividade não importados irão perder-se. + Atividade antiga foi apagada com sucesso. + Não foi possível apagar a atividade antiga da base de dados. Escrever por cima Cancelar Apagar @@ -222,7 +345,20 @@ Vibração A emparelhar Pebble + É esperado que veja uma notificação de emparelhamento no seu dispositivo Android. Se isso não acontecer, aceda às notificações e aceite o pedido de emparelhamento. Depois aceite igualmente o pedido de emparelhamento no seu Pebble + Garanta que este tema se encontra ativado na Aplicação de Meteorologia para obter informação meteorológica atualizada no seu Pebble.\n\nNão é necessária qualquer configuração aqui.\n\nPode ativar a aplicação nativa de meteorologia do seu Pebble através da gestão de aplicações.\n\nTemas de relógio irão apresentar a informação meteorológica automaticamente. + Ativar o emparelhamento Bluetooth + Desative isto caso tenha problemas na ligação Métrico Imperial + 24H + Manhã/Tarde Alarme + (%1$s) + Encontrado! + Mi2: Formato da hora + Deve instalar a versão %1$s antes de instalar este firmware! + Notificações de texto + = 1.0.1.28 e Mili_pro.ft* instalado.]]> + desligado From 3860c2f9c487318a5df1c25b59a3f2af8a2bf186 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 9 Apr 2017 22:39:50 +0200 Subject: [PATCH 16/40] update changelog, bump versions --- CHANGELOG.md | 3 ++- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 730debff7..d43013c77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ ###Changelog -###Version next +###Version 0.18.5 * Applied some material design guidelines to Charts and (pebble) app management * Changed colours: deep sleep is now dark blue, light sleep is now light blue * Support for exporting and importing of preferences in addition to the database * Visual improvements of the pie charts * Add filter by name in the App blacklist activity * Pebble: improve compatibility with watch app configuration pages +* Pebble: display battery percentage (will only update once an hour) * HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) * HPlus: display battery state and warn on low battery diff --git a/app/build.gradle b/app/build.gradle index 44f50e4ee..ce8ef8abf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 25 // note: always bump BOTH versionCode and versionName! - versionName "0.18.4" - versionCode 91 + versionName "0.18.5" + versionCode 92 vectorDrawables.useSupportLibrary = true } buildTypes { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 2365e27e8..bea25db6d 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,12 +1,13 @@ - + Applied some material design guidelines to Charts and (pebble) app management Changed colours: deep sleep is now dark blue, light sleep is now light blue Support for exporting and importing of preferences in addition to the database Visual improvements of the pie charts Add filter by name in the App blacklist activity Pebble: improve compatibility with watch app configuration pages + Pebble: display battery percentage (will only update once an hour) HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642) HPlus: display battery state and warn on low battery From c93e97f10f96ea2e61a16a339e189bcc49da8c6e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 10 Apr 2017 21:36:44 +0200 Subject: [PATCH 17/40] Shave ~600KB off the apk size with the help of a FrankenDAO We use a patched version of greenDAO's DaoGenerator with a bugfix and additional support for - composite primary keys - WITHOUT_ROWID We used to use jitpack for including our own greendao dependency into Gadgetbridge. Since jitpack does not know how to build a real greenDAO release, it will simply include all artifacts, including ftl templates etc. which we do not need at runtime at all. We could fix this by patching greenDAO build, but we actually don't need to do that, since we can simply use the pristine greenDAO runtime dependency. It will happily use our custom DAOGenerator-generated entities. --- app/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ce8ef8abf..5e4b69d0e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,7 +75,9 @@ dependencies { compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' compile 'net.e175.klaus:solarpositioning:0.0.9' - compile 'com.github.freeyourgadget:greendao:1998d7cd2d21f662c6044f6ccf3b3a251bbad341' + // use pristine greendao instead of our custom version, since our custom jitpack-packaged + // version contains way too much and our custom patches are in the generator only. + compile 'org.greenrobot:greendao:2.2.1' compile 'org.apache.commons:commons-lang3:3.5' // compile project(":DaoCore") From 6ed40a21c61bfe54fb7fc7d2c3dbaace77ec01b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 12 Apr 2017 14:29:24 +0100 Subject: [PATCH 18/40] HPlus: convert text to either GB2312 or UTF-8 --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 3adf1345a..16f1660ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -800,7 +800,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { cs = new byte[]{HPlusConstants.transliterateMap.get(c)}; } else { try { - cs = c.toString().getBytes("GB2312"); + if (HPlusCoordinator.getLanguage(this.gbDevice.getAddress()) == HPlusConstants.ARG_LANGUAGE_CN) + cs = c.toString().getBytes("GB2312"); + else + cs = c.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { //Fallback. Result string may be strange, but better than nothing cs = c.toString().getBytes(); From 589945f234caa0b30e957cd25b8df230f91d75c0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 12 Apr 2017 20:48:28 +0200 Subject: [PATCH 19/40] Use try-with-resources to ensure stream is closed on exception --- .../activities/appmanager/AppManagerActivity.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java index 4d16119ee..ded03ab12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java @@ -176,14 +176,11 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { static synchronized void rewriteAppOrderFile(String filename, List uuids) { - try { - FileWriter fileWriter = new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename); - BufferedWriter out = new BufferedWriter(fileWriter); + try (BufferedWriter out = new BufferedWriter(new FileWriter(FileUtils.getExternalFilesDir() + "/" + filename))) { for (UUID uuid : uuids) { out.write(uuid.toString()); out.newLine(); } - out.close(); } catch (IOException e) { LOG.warn("can't write app order to file!"); } @@ -199,8 +196,7 @@ public class AppManagerActivity extends AbstractGBFragmentActivity { static synchronized ArrayList getUuidsFromFile(String filename) { ArrayList uuids = new ArrayList<>(); - try (FileReader fileReader = new FileReader(FileUtils.getExternalFilesDir() + "/" + filename)) { - BufferedReader in = new BufferedReader(fileReader); + try (BufferedReader in = new BufferedReader(new FileReader(FileUtils.getExternalFilesDir() + "/" + filename))) { String line; while ((line = in.readLine()) != null) { uuids.add(UUID.fromString(line)); From 16af0724dd3155d766ff4e094c36d35b78926bad Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 12 Apr 2017 21:33:19 +0200 Subject: [PATCH 20/40] Replace method pair() with connectFirstTime() Should help with #642 for hplus which did not implement pair() --- .../gadgetbridge/impl/GBDeviceService.java | 4 +-- .../gadgetbridge/model/DeviceService.java | 2 +- .../service/AbstractDeviceSupport.java | 8 +++++ .../service/DeviceCommunicationService.java | 8 ++--- .../gadgetbridge/service/DeviceSupport.java | 30 ++++++++++++++----- .../service/ServiceDeviceSupport.java | 10 +++---- .../service/devices/hplus/HPlusSupport.java | 6 ---- .../service/devices/miband/MiBandSupport.java | 5 ++-- .../devices/miband2/MiBand2Support.java | 8 ++--- .../vibratissimo/VibratissimoSupport.java | 5 ---- .../serial/AbstractSerialDeviceSupport.java | 6 ---- .../service/TestDeviceSupport.java | 5 ---- 12 files changed, 47 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 7d3428a1c..fab25272f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -105,10 +105,10 @@ public class GBDeviceService implements DeviceService { } @Override - public void connect(@Nullable GBDevice device, boolean performPair) { + public void connect(@Nullable GBDevice device, boolean firstTime) { Intent intent = createIntent().setAction(ACTION_CONNECT) .putExtra(GBDevice.EXTRA_DEVICE, device) - .putExtra(EXTRA_PERFORM_PAIR, performPair); + .putExtra(EXTRA_CONNECT_FIRST_TIME, firstTime); invokeService(intent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index b81094971..5c126bdd8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -97,7 +97,7 @@ public interface DeviceService extends EventHandler { String EXTRA_URI = "uri"; String EXTRA_CONFIG = "config"; String EXTRA_ALARMS = "alarms"; - String EXTRA_PERFORM_PAIR = "perform_pair"; + String EXTRA_CONNECT_FIRST_TIME = "connect_first_time"; String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; String EXTRA_WEATHER_TIMESTAMP = "weather_timestamp"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index 5f095771d..612f57e2b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -88,6 +88,14 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { this.context = context; } + /** + * Default implementation just calls #connect() + */ + @Override + public boolean connectFirstTime() { + return connect(); + } + @Override public boolean isConnected() { return gbDevice.isConnected(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 741c8cca5..7fb8a4d68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -134,7 +134,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TYPE; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTCONDITION; @@ -239,7 +239,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere } String action = intent.getAction(); - boolean pair = intent.getBooleanExtra(EXTRA_PERFORM_PAIR, false); + boolean firstTime = intent.getBooleanExtra(EXTRA_CONNECT_FIRST_TIME, false); if (action == null) { LOG.info("no action"); @@ -299,8 +299,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere DeviceSupport deviceSupport = mFactory.createDeviceSupport(gbDevice); if (deviceSupport != null) { setDeviceSupport(deviceSupport); - if (pair) { - deviceSupport.pair(); + if (firstTime) { + deviceSupport.connectFirstTime(); } else { deviceSupport.setAutoReconnect(autoReconnect); deviceSupport.connect(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java index 2adeccde9..087019ccc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java @@ -52,6 +52,27 @@ public interface DeviceSupport extends EventHandler { */ boolean isConnected(); + /** + * Attempts an initial connection to the device, typically after the user "discovered" + * and connects to it for the first time. Some implementations may perform an additional + * initialization or application-level pairing compared to the regular {@link #connect()}. + *

+ * Implementations may perform the connection in a synchronous or asynchronous way. + * Returns true if a connection attempt was made. If the implementation is synchronous + * it may also return true if the connection was successfully established, however + * callers shall not rely on that. + *

+ * The actual connection state change (successful or not) will be reported via the + * #getDevice device as a device change Intent. + * + * Note: the default implementation {@link AbstractDeviceSupport#connectFirstTime()} just + * calls {@link #connect()} + * + * @see #connect() + * @see GBDevice#ACTION_DEVICE_CHANGED + */ + boolean connectFirstTime(); + /** * Attempts to establish a connection to the device. Implementations may perform * the connection in a synchronous or asynchronous way. @@ -62,6 +83,7 @@ public interface DeviceSupport extends EventHandler { * The actual connection state change (successful or not) will be reported via the * #getDevice device as a device change Intent. * + * @see #connectFirstTime() * @see GBDevice#ACTION_DEVICE_CHANGED */ boolean connect(); @@ -92,14 +114,6 @@ public interface DeviceSupport extends EventHandler { */ boolean getAutoReconnect(); - /** - * Attempts to pair and connect this device with the gadget device. Success - * will be reported via a device change Intent. - * - * @see GBDevice#ACTION_DEVICE_CHANGED - */ - void pair(); - /** * Returns the associated device this instance communicates with. */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index ea85c9611..26b01e207 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -72,6 +72,11 @@ public class ServiceDeviceSupport implements DeviceSupport { return delegate.isConnected(); } + @Override + public boolean connectFirstTime() { + return delegate.connectFirstTime(); + } + @Override public boolean connect() { return delegate.connect(); @@ -112,11 +117,6 @@ public class ServiceDeviceSupport implements DeviceSupport { return delegate.useAutoConnect(); } - @Override - public void pair() { - delegate.pair(); - } - private boolean checkBusy(String notificationKind) { if (!flags.contains(Flags.BUSY_CHECKING)) { return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 16f1660ca..805c952fe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -417,12 +417,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return true; } - @Override - public void pair() { - - LOG.debug("Pair"); - } - private void handleDeviceInfo(DeviceInfo info) { LOG.warn("Device info: " + info); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index b8827b161..674f2d2a2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -217,12 +217,13 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void pair() { + public boolean connectFirstTime() { for (int i = 0; i < 5; i++) { if (connect()) { - return; + return true; } } + return false; } public DeviceInfo getDeviceInfo() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index 41f6311e8..7663b816a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -267,13 +267,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } @Override - public void pair() { + public boolean connectFirstTime() { needsAuth = true; - for (int i = 0; i < 5; i++) { - if (connect()) { - return; - } - } + return super.connect(); } private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java index df5b5f575..30d8ae362 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java @@ -124,11 +124,6 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport { return true; } - @Override - public void pair() { - - } - private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { LOG.warn("Device info: " + info); versionCmd.hwVersion = info.getHardwareRevision(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 9577154ac..1adf9917a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -68,12 +68,6 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport } } - @Override - public void pair() { - // Default implementation does no manual pairing, use the Android - // pairing dialog instead. - } - /** * Lazily creates and returns the GBDeviceProtocol instance to be used. */ diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 45ba12177..f3239fb70 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -44,11 +44,6 @@ class TestDeviceSupport extends AbstractDeviceSupport { return false; } - @Override - public void pair() { - - } - @Override public void onNotification(NotificationSpec notificationSpec) { From ccb58f0f3c6dc202c9bb1ff2013bbae7787d80fd Mon Sep 17 00:00:00 2001 From: Daniel Hauck Date: Sun, 16 Apr 2017 12:34:37 +0200 Subject: [PATCH 21/40] Basic calendar sync using additional receiver (#654) --- .../externalevents/CalendarReceiver.java | 143 ++++++++++++++++++ .../gadgetbridge/model/CalendarEvents.java | 15 ++ .../service/DeviceCommunicationService.java | 10 ++ 3 files changed, 168 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java new file mode 100644 index 000000000..b09435a05 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -0,0 +1,143 @@ +/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.BuildConfig; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; + +public class CalendarReceiver extends BroadcastReceiver { + private static final Logger LOG = LoggerFactory.getLogger(CalendarReceiver.class); + private static Hashtable eventState = new Hashtable<>(); + + private class EventSyncState { + private EventState state; + private CalendarEvents.CalendarEvent event; + + public EventSyncState(CalendarEvents.CalendarEvent event, EventState state) { + this.state = state; + this.event = event; + } + + public EventState getState() { + return state; + } + + public void setState(EventState state) { + this.state = state; + } + + public CalendarEvents.CalendarEvent getEvent() { + return event; + } + + public void setEvent(CalendarEvents.CalendarEvent event) { + this.event = event; + } + } + + private enum EventState { + NOT_SYNCED, SYNCED, NEEDS_UPDATE, NEEDS_DELETE + } + + public CalendarReceiver() { + LOG.info("Created calendar receiver."); + Context context = GBApplication.getContext(); + Intent intent = new Intent("CALENDAR_SYNC"); + intent.setPackage(BuildConfig.APPLICATION_ID); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent("CALENDAR_SYNC"), 0); + AlarmManager am = (AlarmManager) (context.getSystemService(Context.ALARM_SERVICE)); + + am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent); + } + + @Override + public void onReceive(Context context, Intent intent) { + LOG.info("Syncing with calendar."); + List eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext()); + Hashtable eventTable = new Hashtable<>(); + + for (CalendarEvents.CalendarEvent e: eventList) { + eventTable.put(e.getId(), e); + if (!eventState.containsKey(e.getId())) { + eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED)); + } + } + + Enumeration ids = eventState.keys(); + while (ids.hasMoreElements()) { + Long i = ids.nextElement(); + EventSyncState es = eventState.get(i); + if (eventTable.containsKey(i)) { + if (es.getState() == EventState.SYNCED) { + if (!es.getEvent().equals(eventTable.get(i))) { + eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE)); + } + } + } else { + if (es.getState() == EventState.NOT_SYNCED) { + eventState.remove(i); + } else { + es.setState(EventState.NEEDS_DELETE); + eventState.put(i, es); + } + } + } + + updateEvents(); + } + + private void updateEvents() { + Enumeration ids = eventState.keys(); + while (ids.hasMoreElements()) { + Long i = ids.nextElement(); + EventSyncState es = eventState.get(i); + if ((es.getState() == EventState.NOT_SYNCED) || (es.getState() == EventState.NEEDS_UPDATE)) { + CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); + calendarEventSpec.title = es.getEvent().getTitle(); + calendarEventSpec.id = i; + calendarEventSpec.timestamp = es.getEvent().getBeginSeconds(); + calendarEventSpec.description = es.getEvent().getDescription(); + calendarEventSpec.type = CalendarEventSpec.TYPE_UNKNOWN; + GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); + GBApplication.deviceService().onAddCalendarEvent(calendarEventSpec); + es.setState(EventState.SYNCED); + eventState.put(i, es); + } else if (es.getState() == EventState.NEEDS_DELETE) { + GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); + eventState.remove(i); + } + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java index 63935aa4c..6918eae6b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; +import java.util.Objects; public class CalendarEvents { @@ -155,5 +156,19 @@ public class CalendarEvents { return calName; } + @Override public boolean equals(Object other) { + if (other instanceof CalendarEvent) { + CalendarEvent e = (CalendarEvent) other; + return (this.getId() == e.getId()) && + Objects.equals(this.getTitle(), e.getTitle()) && + (this.getBegin() == e.getBegin()) && + Objects.equals(this.getLocation(), e.getLocation()) && + Objects.equals(this.getDescription(), e.getDescription()) && + (this.getEnd() == e.getEnd()) && + Objects.equals(this.getCalName(), e.getCalName()); + } else { + return false; + } + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 7fb8a4d68..9a3f7d637 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -44,6 +44,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; +import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PebbleReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PhoneCallReceiver; @@ -167,6 +168,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private AlarmClockReceiver mAlarmClockReceiver = null; private AlarmReceiver mAlarmReceiver = null; + private CalendarReceiver mCalendarReceiver = null; private Random mRandom = new Random(); private final String[] mMusicActions = { @@ -613,6 +615,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mAlarmReceiver = new AlarmReceiver(); registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } + if (mCalendarReceiver == null) { + mCalendarReceiver = new CalendarReceiver(); + registerReceiver(mCalendarReceiver, new IntentFilter("CALENDAR_SYNC")); + } if (mAlarmClockReceiver == null) { mAlarmClockReceiver = new AlarmClockReceiver(); IntentFilter filter = new IntentFilter(); @@ -649,6 +655,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mAlarmReceiver); mAlarmReceiver = null; } + if (mCalendarReceiver != null) { + unregisterReceiver(mCalendarReceiver); + mCalendarReceiver = null; + } if (mAlarmClockReceiver != null) { unregisterReceiver(mAlarmClockReceiver); mAlarmClockReceiver = null; From 9970f8017f3b9c04cc3b50cee1eb3d8c78a6196c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 16 Apr 2017 19:37:43 +0200 Subject: [PATCH 22/40] Calendar sync: save sync status to db to avoid leakage of deleted events We now have a per device database that tracks sync states for calendar entries We still cannot track changed calendar entries that where changed while we were disconnected --- .../gadgetbridge/daogen/GBDaoGenerator.java | 19 +- .../externalevents/CalendarReceiver.java | 165 ++++++++++++------ .../service/DeviceCommunicationService.java | 4 +- 3 files changed, 136 insertions(+), 52 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index d70679a1f..9927a60fd 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -17,6 +17,7 @@ package nodomain.freeyourgadget.gadgetbridge.daogen; import de.greenrobot.daogenerator.DaoGenerator; import de.greenrobot.daogenerator.Entity; +import de.greenrobot.daogenerator.Index; import de.greenrobot.daogenerator.Property; import de.greenrobot.daogenerator.Schema; @@ -39,8 +40,9 @@ public class GBDaoGenerator { private static final String TIMESTAMP_FROM = "timestampFrom"; private static final String TIMESTAMP_TO = "timestampTo"; + public static void main(String[] args) throws Exception { - Schema schema = new Schema(15, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(17, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -63,6 +65,8 @@ public class GBDaoGenerator { addHPlusHealthActivityKindOverlay(schema, user, device); addHPlusHealthActivitySample(schema, user, device); + addCalendarSyncState(schema, device); + new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -267,6 +271,19 @@ public class GBDaoGenerator { activitySample.addToOne(user, userId); } + private static void addCalendarSyncState(Schema schema, Entity device) { + Entity calendarSyncState = addEntity(schema, "CalendarSyncState"); + Property deviceId = calendarSyncState.addLongProperty("deviceId").notNull().getProperty(); + Property calendarEntryId = calendarSyncState.addLongProperty("calendarEntryId").notNull().getProperty(); + Index indexUnique = new Index(); + indexUnique.addProperty(deviceId); + indexUnique.addProperty(calendarEntryId); + indexUnique.makeUnique(); + calendarSyncState.addIndex(indexUnique); + calendarSyncState.addToOne(device, deviceId); + calendarSyncState.addIntProperty("syncState").notNull(); + } + private static Property findProperty(Entity entity, String propertyName) { for (Property prop : entity.getProperties()) { if (propertyName.equals(prop.getPropertyName())) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index b09435a05..b81b417e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -22,39 +22,48 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.Calendar; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; -import nodomain.freeyourgadget.gadgetbridge.BuildConfig; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncState; +import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; +import nodomain.freeyourgadget.gadgetbridge.util.GB; public class CalendarReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(CalendarReceiver.class); - private static Hashtable eventState = new Hashtable<>(); + private Hashtable eventState = new Hashtable<>(); + + private GBDevice mGBDevice; private class EventSyncState { - private EventState state; + private int state; private CalendarEvents.CalendarEvent event; - public EventSyncState(CalendarEvents.CalendarEvent event, EventState state) { + EventSyncState(CalendarEvents.CalendarEvent event, int state) { this.state = state; this.event = event; } - public EventState getState() { + public int getState() { return state; } - public void setState(EventState state) { + public void setState(int state) { this.state = state; } @@ -67,76 +76,134 @@ public class CalendarReceiver extends BroadcastReceiver { } } - private enum EventState { - NOT_SYNCED, SYNCED, NEEDS_UPDATE, NEEDS_DELETE + private static class EventState { + private static final int NOT_SYNCED = 0; + private static final int SYNCED = 1; + private static final int NEEDS_UPDATE = 2; + private static final int NEEDS_DELETE = 3; } - public CalendarReceiver() { + public CalendarReceiver(GBDevice gbDevice) { LOG.info("Created calendar receiver."); + mGBDevice = gbDevice; Context context = GBApplication.getContext(); - Intent intent = new Intent("CALENDAR_SYNC"); - intent.setPackage(BuildConfig.APPLICATION_ID); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent("CALENDAR_SYNC"), 0); AlarmManager am = (AlarmManager) (context.getSystemService(Context.ALARM_SERVICE)); - am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent); + + //FIXME: 30 sec interval is only for debugging + am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, 30000, pendingIntent); + + //am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent); + //syncCalendar(); - does not work here (device not yet initialized) } @Override public void onReceive(Context context, Intent intent) { - LOG.info("Syncing with calendar."); - List eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext()); - Hashtable eventTable = new Hashtable<>(); - - for (CalendarEvents.CalendarEvent e: eventList) { - eventTable.put(e.getId(), e); - if (!eventState.containsKey(e.getId())) { - eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED)); - } - } - - Enumeration ids = eventState.keys(); - while (ids.hasMoreElements()) { - Long i = ids.nextElement(); - EventSyncState es = eventState.get(i); - if (eventTable.containsKey(i)) { - if (es.getState() == EventState.SYNCED) { - if (!es.getEvent().equals(eventTable.get(i))) { - eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE)); - } - } - } else { - if (es.getState() == EventState.NOT_SYNCED) { - eventState.remove(i); - } else { - es.setState(EventState.NEEDS_DELETE); - eventState.put(i, es); - } - } - } - - updateEvents(); + syncCalendar(); } - private void updateEvents() { + public void syncCalendar() { + LOG.info("Syncing with calendar."); + List eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext()); + Hashtable eventTable = new Hashtable<>(); + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + Long deviceId = DBHelper.getDevice(mGBDevice, session).getId(); + + QueryBuilder qb = session.getCalendarSyncStateDao().queryBuilder(); + + + for (CalendarEvents.CalendarEvent e : eventList) { + eventTable.put(e.getId(), e); + if (!eventState.containsKey(e.getId())) { + qb = session.getCalendarSyncStateDao().queryBuilder(); + + CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(e.getId()))) + .build().unique(); + if (calendarSyncState == null) { + eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED)); + } else { + eventState.put(e.getId(), new EventSyncState(e, calendarSyncState.getSyncState())); + } + } + } + + // add all missing calendar ids on the device to sync status (so that they are deleted later) + List CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list(); + for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) { + if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) { + eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, CalendarSyncState.getSyncState())); + LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName()); + } + } + + Enumeration ids = eventState.keys(); + while (ids.hasMoreElements()) { + qb = session.getCalendarSyncStateDao().queryBuilder(); + Long i = ids.nextElement(); + EventSyncState es = eventState.get(i); + if (eventTable.containsKey(i)) { + if (es.getState() == EventState.SYNCED) { + if (!es.getEvent().equals(eventTable.get(i))) { + eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE)); + // update sync status of that Calendar entry in DB for all devices + CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)).build().list(); + for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) { + CalendarSyncState.setSyncState(EventState.NEEDS_UPDATE); + CalendarSyncState.update(); + } + } + } + } else { + if (es.getState() == EventState.NOT_SYNCED) { + // delete for current device only + qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i))) + .buildDelete().executeDeleteWithoutDetachingEntities(); + eventState.remove(i); + } else { + es.setState(EventState.NEEDS_DELETE); + eventState.put(i, es); + } + } + updateEvents(deviceId, session); + } + } catch (Exception e) { + e.printStackTrace(); + GB.toast("Datebase Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR); + } + + } + + private void updateEvents(Long deviceId, DaoSession session) { Enumeration ids = eventState.keys(); while (ids.hasMoreElements()) { Long i = ids.nextElement(); EventSyncState es = eventState.get(i); - if ((es.getState() == EventState.NOT_SYNCED) || (es.getState() == EventState.NEEDS_UPDATE)) { + int syncState = es.getState(); + if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) { CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); calendarEventSpec.title = es.getEvent().getTitle(); calendarEventSpec.id = i; calendarEventSpec.timestamp = es.getEvent().getBeginSeconds(); calendarEventSpec.description = es.getEvent().getDescription(); calendarEventSpec.type = CalendarEventSpec.TYPE_UNKNOWN; - GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); + if (syncState == EventState.NEEDS_UPDATE) { + GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); + } GBApplication.deviceService().onAddCalendarEvent(calendarEventSpec); es.setState(EventState.SYNCED); eventState.put(i, es); - } else if (es.getState() == EventState.NEEDS_DELETE) { + // update db + session.insertOrReplace(new CalendarSyncState(deviceId, i, EventState.SYNCED)); + } else if (syncState == EventState.NEEDS_DELETE) { GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); eventState.remove(i); + // delete from db for current device only + QueryBuilder qb = session.getCalendarSyncStateDao().queryBuilder(); + qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i))) + .buildDelete().executeDeleteWithoutDetachingEntities(); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 9a3f7d637..48bdc7800 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -114,6 +114,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAL import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CANNEDMESSAGES_TYPE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONFIG; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; @@ -135,7 +136,6 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TYPE; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER_CURRENTCONDITION; @@ -616,7 +616,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } if (mCalendarReceiver == null) { - mCalendarReceiver = new CalendarReceiver(); + mCalendarReceiver = new CalendarReceiver(mGBDevice); registerReceiver(mCalendarReceiver, new IntentFilter("CALENDAR_SYNC")); } if (mAlarmClockReceiver == null) { From 67f035accfac4554dedfae1a9d3b4109ecccf085 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 16 Apr 2017 19:43:53 +0200 Subject: [PATCH 23/40] DBHelper: no not update device attributes in db if we call getDevice() on a disconnected device Fixes NPE --- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 22997a429..55d5bee0b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -384,7 +384,9 @@ public class DBHelper { } else { ensureDeviceUpToDate(device, gbDevice, session); } - ensureDeviceAttributes(device, gbDevice, session); + if (gbDevice.isInitialized()) { + ensureDeviceAttributes(device, gbDevice, session); + } return device; } From df0e77f368479f6605730f1cc62e210bd1a991c6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 16 Apr 2017 21:08:49 +0200 Subject: [PATCH 24/40] Calendar Sync: Make greendao .update() work by adding a PK id :/ --- .../gadgetbridge/daogen/GBDaoGenerator.java | 1 + .../gadgetbridge/externalevents/CalendarReceiver.java | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 9927a60fd..12c5ff3fc 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -273,6 +273,7 @@ public class GBDaoGenerator { private static void addCalendarSyncState(Schema schema, Entity device) { Entity calendarSyncState = addEntity(schema, "CalendarSyncState"); + calendarSyncState.addIdProperty(); Property deviceId = calendarSyncState.addLongProperty("deviceId").notNull().getProperty(); Property calendarEntryId = calendarSyncState.addLongProperty("calendarEntryId").notNull().getProperty(); Index indexUnique = new Index(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index b81b417e2..410c0ffec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -183,11 +183,13 @@ public class CalendarReceiver extends BroadcastReceiver { EventSyncState es = eventState.get(i); int syncState = es.getState(); if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) { + CalendarEvents.CalendarEvent calendarEvent = es.getEvent(); CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); - calendarEventSpec.title = es.getEvent().getTitle(); calendarEventSpec.id = i; - calendarEventSpec.timestamp = es.getEvent().getBeginSeconds(); - calendarEventSpec.description = es.getEvent().getDescription(); + calendarEventSpec.title = calendarEvent.getTitle(); + calendarEventSpec.timestamp = calendarEvent.getBeginSeconds(); + //calendarEventSpec.durationInSeconds = calendarEvent.getDurationSeconds(); //FIXME: leads to problems right now + calendarEventSpec.description = calendarEvent.getDescription(); calendarEventSpec.type = CalendarEventSpec.TYPE_UNKNOWN; if (syncState == EventState.NEEDS_UPDATE) { GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); @@ -196,7 +198,7 @@ public class CalendarReceiver extends BroadcastReceiver { es.setState(EventState.SYNCED); eventState.put(i, es); // update db - session.insertOrReplace(new CalendarSyncState(deviceId, i, EventState.SYNCED)); + session.insertOrReplace(new CalendarSyncState(null, deviceId, i, EventState.SYNCED)); } else if (syncState == EventState.NEEDS_DELETE) { GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); eventState.remove(i); From 0cf36253049e186467c06331245b59cf125bbaea Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 17 Apr 2017 15:12:25 +0200 Subject: [PATCH 25/40] Remove the Pebble Time 2 Pebble Time 2 is unreleased / untested, better leave it out from the supported devices list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 080f44a9f..113a2eec3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ need to create an account and transmit any of your data to the vendor's servers. ## Supported Devices * Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki section about this device](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble) -* Pebble 2, Pebble Time 2 (experimental, PAIR WITHIN GADGETBRIDGE) [Wiki section about pebble](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble), most parts apply to Pebble 2 as well +* Pebble 2 (add the device from within Gadgetbridge!) [Wiki section about pebble](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble), most parts apply to Pebble 2 as well * Mi Band, Mi Band 1A, Mi Band 1S [Wiki section about this device](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band) * Mi Band 2 [Wiki section about mi band](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band), some parts apply to mi band 2 as well * Vibratissimo (experimental) From a936ff06168ec792da7c61c5bb830bb27a055ea5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 17 Apr 2017 19:19:23 +0200 Subject: [PATCH 26/40] Tests: set device state to inizialized before assuming fw version gets written to attributes --- .../freeyourgadget/gadgetbridge/database/EntitiesTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java index fcd5fc09d..44f99f4ac 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java @@ -178,6 +178,8 @@ public class EntitiesTest extends TestBase { public void testDeviceAttributes() throws Exception { GBDevice dummyGBDevice = createDummyGDevice("00:00:00:00:02"); dummyGBDevice.setFirmwareVersion("1.0"); + dummyGBDevice.setState(GBDevice.State.INITIALIZED); + Device deviceOld = DBHelper.getDevice(dummyGBDevice, daoSession); assertNotNull(deviceOld); From 60b7a73558141cc30fde6c356008da53400b6ad9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 17 Apr 2017 19:36:50 +0200 Subject: [PATCH 27/40] Test: fix other test --- .../freeyourgadget/gadgetbridge/database/EntitiesTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java index 44f99f4ac..7c4f47461 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/database/EntitiesTest.java @@ -82,6 +82,7 @@ public class EntitiesTest extends TestBase { @Test public void testDBHelper() { GBDevice dummyGBDevice = createDummyGDevice("00:00:00:00:01"); + dummyGBDevice.setState(GBDevice.State.INITIALIZED); Device device = DBHelper.getDevice(dummyGBDevice, daoSession); assertNotNull(device); assertEquals("00:00:00:00:01", device.getIdentifier()); From 61690eb2cca55d7ce8a9607754f4c1fb226735e6 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 17 Apr 2017 20:26:33 +0200 Subject: [PATCH 28/40] Get notified when calendar events change instead of polling. --- .../externalevents/CalendarReceiver.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index 410c0ffec..c23004b8f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -17,17 +17,15 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; -import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Calendar; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; @@ -87,15 +85,19 @@ public class CalendarReceiver extends BroadcastReceiver { LOG.info("Created calendar receiver."); mGBDevice = gbDevice; Context context = GBApplication.getContext(); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent("CALENDAR_SYNC"), 0); - AlarmManager am = (AlarmManager) (context.getSystemService(Context.ALARM_SERVICE)); + IntentFilter calendarIntentFilter = new IntentFilter(); + calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); + calendarIntentFilter.addDataScheme("content"); + calendarIntentFilter.addDataAuthority("com.android.calendar", null); - //FIXME: 30 sec interval is only for debugging - am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, 30000, pendingIntent); - - //am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, AlarmManager.INTERVAL_HALF_HOUR, pendingIntent); - //syncCalendar(); - does not work here (device not yet initialized) + BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + syncCalendar(); + } + }; + context.registerReceiver(receiver, calendarIntentFilter); } @Override From 1e231e612958b3c6fa029100ada1c3b5ca786b80 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 17 Apr 2017 20:33:39 +0200 Subject: [PATCH 29/40] Move the Calendar receiver code in the proper place. --- .../externalevents/CalendarReceiver.java | 15 --------------- .../service/DeviceCommunicationService.java | 6 +++++- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index c23004b8f..d4f22439a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -20,7 +20,6 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.widget.Toast; import org.slf4j.Logger; @@ -84,20 +83,6 @@ public class CalendarReceiver extends BroadcastReceiver { public CalendarReceiver(GBDevice gbDevice) { LOG.info("Created calendar receiver."); mGBDevice = gbDevice; - Context context = GBApplication.getContext(); - - IntentFilter calendarIntentFilter = new IntentFilter(); - calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); - calendarIntentFilter.addDataScheme("content"); - calendarIntentFilter.addDataAuthority("com.android.calendar", null); - - BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - syncCalendar(); - } - }; - context.registerReceiver(receiver, calendarIntentFilter); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 48bdc7800..a1a4ab19e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -616,8 +616,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } if (mCalendarReceiver == null) { + IntentFilter calendarIntentFilter = new IntentFilter(); + calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); + calendarIntentFilter.addDataScheme("content"); + calendarIntentFilter.addDataAuthority("com.android.calendar", null); mCalendarReceiver = new CalendarReceiver(mGBDevice); - registerReceiver(mCalendarReceiver, new IntentFilter("CALENDAR_SYNC")); + registerReceiver(mCalendarReceiver, calendarIntentFilter); } if (mAlarmClockReceiver == null) { mAlarmClockReceiver = new AlarmClockReceiver(); From 7b50ba95727ad130e734802d8f0772107d984a76 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 17 Apr 2017 21:07:50 +0200 Subject: [PATCH 30/40] Implement hashCode() as equals has been implemented. --- .../gadgetbridge/model/CalendarEvents.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java index 6918eae6b..8172dc138 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java @@ -156,7 +156,8 @@ public class CalendarEvents { return calName; } - @Override public boolean equals(Object other) { + @Override + public boolean equals(Object other) { if (other instanceof CalendarEvent) { CalendarEvent e = (CalendarEvent) other; return (this.getId() == e.getId()) && @@ -170,5 +171,17 @@ public class CalendarEvents { return false; } } + + @Override + public int hashCode() { + int result = (int) id; + result = 31 * result + title.hashCode(); + result = 31 * result + Long.valueOf(begin).hashCode(); + result = 31 * result + location.hashCode(); + result = 31 * result + description.hashCode(); + result = 31 * result + Long.valueOf(end).hashCode(); + result = 31 * result + calName.hashCode(); + return result; + } } } From 35efa30c4b5d684794ff5381815e38af89eb73c2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 17 Apr 2017 21:09:29 +0200 Subject: [PATCH 31/40] Calendar Sync: Only enable calendar broadcast receiver when device is initialized This excludes it from any auto connect logic. We are now save to sync the calendar once in contructor. --- .../externalevents/CalendarReceiver.java | 2 ++ .../service/DeviceCommunicationService.java | 34 ++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index d4f22439a..4918e6955 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -83,10 +83,12 @@ public class CalendarReceiver extends BroadcastReceiver { public CalendarReceiver(GBDevice gbDevice) { LOG.info("Created calendar receiver."); mGBDevice = gbDevice; + syncCalendar(); } @Override public void onReceive(Context context, Intent intent) { + LOG.info("got calendar changed broadcast"); syncCalendar(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index a1a4ab19e..a99fc6551 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -204,7 +204,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (mGBDevice.equals(device)) { mGBDevice = device; boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); - setReceiversEnableState(enableReceivers); + setReceiversEnableState(enableReceivers, mGBDevice.isInitialized()); GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context); } else { LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + mGBDevice); @@ -395,7 +395,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere case ACTION_DISCONNECT: { mDeviceSupport.dispose(); if (mGBDevice != null && mGBDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) { - setReceiversEnableState(false); + setReceiversEnableState(false, false); mGBDevice.setState(GBDevice.State.NOT_CONNECTED); mGBDevice.sendDeviceUpdateIntent(this); } @@ -573,9 +573,23 @@ public class DeviceCommunicationService extends Service implements SharedPrefere } - private void setReceiversEnableState(boolean enable) { + private void setReceiversEnableState(boolean enable, boolean initialized) { LOG.info("Setting broadcast receivers to: " + enable); + if (enable && initialized) { + if (mCalendarReceiver == null) { + IntentFilter calendarIntentFilter = new IntentFilter(); + calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); + calendarIntentFilter.addDataScheme("content"); + calendarIntentFilter.addDataAuthority("com.android.calendar", null); + mCalendarReceiver = new CalendarReceiver(mGBDevice); + registerReceiver(mCalendarReceiver, calendarIntentFilter); + } + } else if (mCalendarReceiver != null) { + unregisterReceiver(mCalendarReceiver); + mCalendarReceiver = null; + } + if (enable) { if (mPhoneCallReceiver == null) { mPhoneCallReceiver = new PhoneCallReceiver(); @@ -615,14 +629,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mAlarmReceiver = new AlarmReceiver(); registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } - if (mCalendarReceiver == null) { - IntentFilter calendarIntentFilter = new IntentFilter(); - calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); - calendarIntentFilter.addDataScheme("content"); - calendarIntentFilter.addDataAuthority("com.android.calendar", null); - mCalendarReceiver = new CalendarReceiver(mGBDevice); - registerReceiver(mCalendarReceiver, calendarIntentFilter); - } if (mAlarmClockReceiver == null) { mAlarmClockReceiver = new AlarmClockReceiver(); IntentFilter filter = new IntentFilter(); @@ -659,10 +665,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mAlarmReceiver); mAlarmReceiver = null; } - if (mCalendarReceiver != null) { - unregisterReceiver(mCalendarReceiver); - mCalendarReceiver = null; - } if (mAlarmClockReceiver != null) { unregisterReceiver(mAlarmClockReceiver); mAlarmClockReceiver = null; @@ -680,7 +682,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); - setReceiversEnableState(false); // disable BroadcastReceivers + setReceiversEnableState(false, false); // disable BroadcastReceivers setDeviceSupport(null); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); From b142add631bd685ed290a8e5c4879670658195fd Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 17 Apr 2017 23:00:16 +0200 Subject: [PATCH 32/40] Pass a GBDevice instead of GBDeviceCandidate to getBondingStyle() #651 --- .../gadgetbridge/activities/DiscoveryActivity.java | 2 +- .../gadgetbridge/devices/AbstractDeviceCoordinator.java | 2 +- .../gadgetbridge/devices/DeviceCoordinator.java | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index b0a5f04af..6c80cf76a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -584,7 +584,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC startActivity(intent); } else { GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate); - int bondingStyle = coordinator.getBondingStyle(deviceCandidate); + int bondingStyle = coordinator.getBondingStyle(device); if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) { LOG.info("No bonding needed, according to coordinator, so connecting right away"); connectAndFinish(device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index 055156a43..7c4e04124 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -121,7 +121,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { } @Override - public int getBondingStyle(GBDeviceCandidate deviceCandidate) { + public int getBondingStyle(GBDevice device) { return BONDING_STYLE_ASK; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index 9b74d3f7c..515a6e23d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -23,7 +23,6 @@ import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; import android.os.Build; -import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -225,7 +224,7 @@ public interface DeviceCoordinator { /** * Returns how/if the given device should be bonded before connecting to it. - * @param deviceCandidate + * @param device */ - int getBondingStyle(GBDeviceCandidate deviceCandidate); + int getBondingStyle(GBDevice device); } From 9f0d260e7a3ae6459df8937a2d6ba7012fffcf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Barraca?= Date: Tue, 18 Apr 2017 10:47:28 +0100 Subject: [PATCH 33/40] HPlus: Improve connection process (#651) * Clean HPlus services and characteristics * Improve connectivity --- .../devices/hplus/HPlusCoordinator.java | 5 + .../BluetoothPairingRequestReceiver.java | 73 ++++++++++++ .../service/DeviceCommunicationService.java | 13 +++ .../service/btle/BleNamesResolver.java | 3 + .../devices/hplus/HPlusDataRecordDaySlot.java | 4 + .../devices/hplus/HPlusHandlerThread.java | 109 +++++++++++++----- .../service/devices/hplus/HPlusSupport.java | 106 +++++++++-------- 7 files changed, 240 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 690934609..0be66cd6f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -80,6 +80,11 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return DeviceType.UNKNOWN; } + @Override + public int getBondingStyle(GBDevice deviceCandidate){ + return BONDING_STYLE_NONE; + } + @Override public DeviceType getDeviceType() { return DeviceType.HPLUS; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java new file mode 100644 index 000000000..f322e7f5b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java @@ -0,0 +1,73 @@ +/* Copyright (C) 2015-2017 João Paulo Barraca + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; + +/** + * Created by jpbarraca on 13/04/2017. + */ + +public class BluetoothPairingRequestReceiver extends BroadcastReceiver { + + + private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class); + + final DeviceCommunicationService service; + + public BluetoothPairingRequestReceiver(DeviceCommunicationService service) { + this.service = service; + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + + if (!action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) { + return; + } + + GBDevice gbDevice = service.getGBDevice(); + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (gbDevice == null || device == null) + return; + + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); + try { + if (coordinator.getBondingStyle(gbDevice) == DeviceCoordinator.BONDING_STYLE_NONE) { + LOG.info("Aborting unwanted pairing request"); + abortBroadcast(); + } + } catch (Exception e) { + LOG.warn("Could not abort pairing request process"); + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 7fb8a4d68..e04133e2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -44,6 +44,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; +import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothPairingRequestReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PebbleReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.PhoneCallReceiver; @@ -164,6 +165,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private MusicPlaybackReceiver mMusicPlaybackReceiver = null; private TimeChangeReceiver mTimeChangeReceiver = null; private BluetoothConnectReceiver mBlueToothConnectReceiver = null; + private BluetoothPairingRequestReceiver mBlueToothPairingRequestReceiver = null; private AlarmClockReceiver mAlarmClockReceiver = null; private AlarmReceiver mAlarmReceiver = null; @@ -609,6 +611,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mBlueToothConnectReceiver = new BluetoothConnectReceiver(this); registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)); } + if (mBlueToothPairingRequestReceiver == null) { + mBlueToothPairingRequestReceiver = new BluetoothPairingRequestReceiver(this); + registerReceiver(mBlueToothPairingRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)); + } + if (mAlarmReceiver == null) { mAlarmReceiver = new AlarmReceiver(); registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); @@ -645,6 +652,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mBlueToothConnectReceiver); mBlueToothConnectReceiver = null; } + + if (mBlueToothPairingRequestReceiver != null) { + unregisterReceiver(mBlueToothPairingRequestReceiver); + mBlueToothPairingRequestReceiver = null; + } + if (mAlarmReceiver != null) { unregisterReceiver(mAlarmReceiver); mAlarmReceiver = null; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java index 66d24cd40..9bdc0f885 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java @@ -101,6 +101,7 @@ public class BleNamesResolver { mServices.put("00001804-0000-1000-8000-00805f9b34fb", "Tx Power"); mServices.put("0000fee0-0000-3512-2118-0009af100700", "(Propr: Xiaomi MiLi Service)"); mServices.put("00001530-0000-3512-2118-0009af100700", "(Propr: Xiaomi Weight Service)"); + mServices.put("14701820-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Service)"); mCharacteristics.put("00002a43-0000-1000-8000-00805f9b34fb", "Alert AlertCategory ID"); @@ -185,6 +186,8 @@ public class BleNamesResolver { mCharacteristics.put("00002a07-0000-1000-8000-00805f9b34fb", "Tx Power Level"); mCharacteristics.put("00002a45-0000-1000-8000-00805f9b34fb", "Unread Alert Status"); + mCharacteristics.put("14702856-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Control)"); + mCharacteristics.put("14702853-620a-3973-7c78-9cfff0876abd", "(Propr: HPLUS Measurements)"); mValueFormats.put(Integer.valueOf(52), "32bit float"); mValueFormats.put(Integer.valueOf(50), "16bit float"); mValueFormats.put(Integer.valueOf(34), "16bit signed int"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java index ee3025823..abee8ff55 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java @@ -102,4 +102,8 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord { secondsInactive += other.secondsInactive; } + + public boolean isValid(){ + return steps != 0 || secondsInactive != 0 || heartRate != -1; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index a8bce80e4..fa8b37440 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -65,6 +65,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60; private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30; + private int HELLO_PERIOD = 60; + private boolean mQuit = false; private HPlusSupport mHPlusSupport; @@ -76,6 +78,8 @@ class HPlusHandlerThread extends GBDeviceIoThread { private Calendar mGetSleepTime = GregorianCalendar.getInstance(); private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance(); + private Calendar mHelloTime = GregorianCalendar.getInstance(); + private boolean mSlotsInitialSync = true; private HPlusDataRecordRealtime prevRealTimeRecord = null; @@ -88,7 +92,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { super(gbDevice, context); - + LOG.info("Initializing HPlus Handler Thread"); mQuit = false; mHPlusSupport = hplusSupport; @@ -137,6 +141,10 @@ class HPlusHandlerThread extends GBDeviceIoThread { requestDaySummaryData(); } + if(now.compareTo(mHelloTime) > 0){ + sendHello(); + } + now = GregorianCalendar.getInstance(); waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis())) - now.getTimeInMillis(); } @@ -152,10 +160,14 @@ class HPlusHandlerThread extends GBDeviceIoThread { } public void sync() { + LOG.info("HPlus: Starting data synchronization"); + mGetSleepTime.setTimeInMillis(0); mGetDaySlotsTime.setTimeInMillis(0); mGetDaySummaryTime.setTimeInMillis(0); mLastSleepDayReceived.setTimeInMillis(0); + mHelloTime = GregorianCalendar.getInstance(); + mHelloTime.add(Calendar.SECOND, HELLO_PERIOD); mSlotsInitialSync = true; mLastSlotReceived = -1; @@ -163,19 +175,41 @@ class HPlusHandlerThread extends GBDeviceIoThread { mCurrentDaySlot = null; mDaySlotRecords.clear(); - TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); + try { + if(!mHPlusSupport.isConnected()) + mHPlusSupport.connect(); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); + TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); - builder.queue(mHPlusSupport.getQueue()); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); + + builder.queue(mHPlusSupport.getQueue()); + }catch(Exception e){ + + } synchronized (waitObject) { waitObject.notify(); } } + public void sendHello(){ + try { + if(!mHPlusSupport.isConnected()) + mHPlusSupport.connect(); + + TransactionBuilder builder = new TransactionBuilder("hello"); + builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO); + builder.queue(mHPlusSupport.getQueue()); + }catch(Exception e){ + + } + mHelloTime = GregorianCalendar.getInstance(); + mHelloTime.add(Calendar.SECOND, HELLO_PERIOD); + + } /** * Process a message containing information regarding a day slot * A slot summarizes 10 minutes of data @@ -190,7 +224,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { try{ record = new HPlusDataRecordDaySlot(data); } catch(IllegalArgumentException e){ - LOG.debug((e.getMessage())); + LOG.info((e.getMessage())); return false; } @@ -254,6 +288,11 @@ class HPlusHandlerThread extends GBDeviceIoThread { List samples = new ArrayList<>(); for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) { + + //Invalid records (no data) will be ignored + if(!storedRecord.isValid()) + continue; + HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp); sample.setRawHPlusHealthData(storedRecord.getRawData()); @@ -269,9 +308,9 @@ class HPlusHandlerThread extends GBDeviceIoThread { mDaySlotRecords.clear(); } catch (GBException ex) { - LOG.debug((ex.getMessage())); + LOG.info((ex.getMessage())); } catch (Exception ex) { - LOG.debug(ex.getMessage()); + LOG.info(ex.getMessage()); } } @@ -293,7 +332,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { try{ record = new HPlusDataRecordSleep(data); } catch(IllegalArgumentException e){ - LOG.debug((e.getMessage())); + LOG.info((e.getMessage())); return false; } @@ -326,7 +365,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { provider.addGBActivitySample(sample); } catch (Exception ex) { - LOG.debug(ex.getMessage()); + LOG.info(ex.getMessage()); } mGetSleepTime = GregorianCalendar.getInstance(); @@ -347,7 +386,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { try{ record = new HPlusDataRecordRealtime(data); } catch(IllegalArgumentException e){ - LOG.debug((e.getMessage())); + LOG.info((e.getMessage())); return false; } @@ -397,9 +436,9 @@ class HPlusHandlerThread extends GBDeviceIoThread { //TODO: Handle Active Time. With Overlay? } catch (GBException ex) { - LOG.debug((ex.getMessage())); + LOG.info((ex.getMessage())); } catch (Exception ex) { - LOG.debug(ex.getMessage()); + LOG.info(ex.getMessage()); } return true; } @@ -417,7 +456,7 @@ class HPlusHandlerThread extends GBDeviceIoThread { try{ record = new HPlusDataRecordDaySummary(data); } catch(IllegalArgumentException e){ - LOG.debug((e.getMessage())); + LOG.info((e.getMessage())); return false; } @@ -437,9 +476,9 @@ class HPlusHandlerThread extends GBDeviceIoThread { sample.setProvider(provider); provider.addGBActivitySample(sample); } catch (GBException ex) { - LOG.debug((ex.getMessage())); + LOG.info((ex.getMessage())); } catch (Exception ex) { - LOG.debug(ex.getMessage()); + LOG.info(ex.getMessage()); } mGetDaySummaryTime = GregorianCalendar.getInstance(); @@ -468,10 +507,16 @@ class HPlusHandlerThread extends GBDeviceIoThread { * Issue a message requesting the next batch of sleep data */ private void requestNextSleepData() { - TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); - builder.queue(mHPlusSupport.getQueue()); + try { + if(!mHPlusSupport.isConnected()) + mHPlusSupport.connect(); + TransactionBuilder builder = new TransactionBuilder("requestSleepStats"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP}); + builder.queue(mHPlusSupport.getQueue()); + }catch(Exception e){ + + } mGetSleepTime = GregorianCalendar.getInstance(); mGetSleepTime.add(GregorianCalendar.SECOND, SLEEP_SYNC_RETRY_PERIOD); @@ -519,19 +564,31 @@ class HPlusHandlerThread extends GBDeviceIoThread { mLastSlotRequested = nextHour * 6 + (nextMinute / 10); byte[] msg = new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY, hour, minute, nextHour, nextMinute}; + try { + if(!mHPlusSupport.isConnected()) + mHPlusSupport.connect(); - TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); - builder.write(mHPlusSupport.ctrlCharacteristic, msg); - builder.queue(mHPlusSupport.getQueue()); + TransactionBuilder builder = new TransactionBuilder("getNextDaySlot"); + builder.write(mHPlusSupport.ctrlCharacteristic, msg); + builder.queue(mHPlusSupport.getQueue()); + }catch(Exception e){ + + } } /** * Request a batch of data with the summary of the previous days */ public void requestDaySummaryData(){ - TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); - builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); - builder.queue(mHPlusSupport.getQueue()); + try { + if(!mHPlusSupport.isConnected()) + mHPlusSupport.connect(); + TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary"); + builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA}); + builder.queue(mHPlusSupport.getQueue()); + }catch(Exception e){ + + } mGetDaySummaryTime = GregorianCalendar.getInstance(); mGetDaySummaryTime.add(Calendar.SECOND, DAY_SUMMARY_SYNC_RETRY_PERIOD); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 805c952fe..3cb3a881d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -94,8 +94,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { deviceType = type; - addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); - addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); addSupportedService(HPlusConstants.UUID_SERVICE_HP); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); @@ -106,7 +104,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void dispose() { - LOG.debug("Dispose"); + LOG.info("Dispose"); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); broadcastManager.unregisterReceiver(mReceiver); @@ -117,7 +115,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { - LOG.debug("Initializing"); + LOG.info("Initializing"); builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); @@ -424,7 +422,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { //TODO: Show different notifications according to source as Band supports this - //LOG.debug("OnNotification: Title: "+notificationSpec.title+" Body: "+notificationSpec.body+" Source: "+notificationSpec.sourceName+" Sender: "+notificationSpec.sender+" Subject: "+notificationSpec.subject); + //LOG.info("OnNotification: Title: "+notificationSpec.title+" Body: "+notificationSpec.body+" Source: "+notificationSpec.sourceName+" Sender: "+notificationSpec.sender+" Subject: "+notificationSpec.subject); showText(notificationSpec.title, notificationSpec.body); } @@ -435,40 +433,46 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetTime() { - TransactionBuilder builder = new TransactionBuilder("time"); + try { + TransactionBuilder builder = performInitialized("time"); - setCurrentDate(builder); - setCurrentTime(builder); + setCurrentDate(builder); + setCurrentTime(builder); - builder.queue(getQueue()); + builder.queue(getQueue()); + }catch(IOException e){ + + } } @Override public void onSetAlarms(ArrayList alarms) { + try { + TransactionBuilder builder = performInitialized("alarm"); - TransactionBuilder builder = new TransactionBuilder("alarm"); + for (Alarm alarm : alarms) { - for (Alarm alarm : alarms) { + if (!alarm.isEnabled()) + continue; - if (!alarm.isEnabled()) - continue; + if (alarm.isSmartWakeup()) //Not available + continue; - if (alarm.isSmartWakeup()) //Not available - continue; + Calendar t = alarm.getAlarmCal(); + setAlarm(builder, t); + builder.queue(getQueue()); - Calendar t = alarm.getAlarmCal(); - setAlarm(builder, t); + GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO); + + return; //Only first alarm + } + + setAlarm(builder, null); builder.queue(getQueue()); - GB.toast(getContext(), getContext().getString(R.string.user_feedback_miband_set_alarms_ok), Toast.LENGTH_SHORT, GB.INFO); + GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO); + }catch(Exception e){} - return; //Only first alarm - } - - setAlarm(builder, null); - builder.queue(getQueue()); - - GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO); } @@ -485,7 +489,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { - LOG.debug("Canned Messages: " + cannedMessagesSpec); + LOG.info("Canned Messages: " + cannedMessagesSpec); } @Override @@ -541,39 +545,47 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onReboot() { - getQueue().clear(); + try { + getQueue().clear(); - TransactionBuilder builder = new TransactionBuilder("Shutdown"); - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN}); - builder.queue(getQueue()); + TransactionBuilder builder = performInitialized("Shutdown"); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN}); + builder.queue(getQueue()); + }catch(Exception e){ + } } @Override public void onHeartRateTest() { getQueue().clear(); + try{ + TransactionBuilder builder = performInitialized("HeartRateTest"); - TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ? + builder.queue(getQueue()); + }catch(Exception e){ - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ? - builder.queue(getQueue()); + } } @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { getQueue().clear(); + try { + TransactionBuilder builder = performInitialized("realTimeHeartMeasurement"); + byte state; - TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); - byte state; + if (enable) + state = HPlusConstants.ARG_HEARTRATE_ALLDAY_ON; + else + state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF; - if (enable) - state = HPlusConstants.ARG_HEARTRATE_ALLDAY_ON; - else - state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF; - - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state}); - builder.queue(getQueue()); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state}); + builder.queue(getQueue()); + }catch(Exception e){ + } } @Override @@ -632,13 +644,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSendConfiguration(String config) { - LOG.debug("Send Configuration: " + config); + LOG.info("Send Configuration: " + config); } @Override public void onTestNewFunction() { - LOG.debug("Test New Function"); + LOG.info("Test New Function"); } @Override @@ -706,7 +718,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } private void showText(String title, String body) { - LOG.debug("Show Notification: " + title + " --> " + body); + try { TransactionBuilder builder = performInitialized("notification"); @@ -844,7 +856,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return syncHelper.processIncomingDaySlotData(data); default: - LOG.debug("Unhandled characteristic changed: " + characteristicUUID); + LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + data[0]); return true; } } @@ -878,7 +890,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getDevice().addDeviceInfo(new GenericItem("", info)); } } catch (IllegalArgumentException e) { - LOG.debug((e.getMessage())); + LOG.info((e.getMessage())); } } From 9decb7788b58ec92686fef094879408a3a1096b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Tue, 18 Apr 2017 10:51:49 +0100 Subject: [PATCH 34/40] HPlus: use HR translated string --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 3cb3a881d..a67a0dd8a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -870,7 +870,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String DEVINFO_STEP = getContext().getString(R.string.chart_steps) + ": "; String DEVINFO_DISTANCE = getContext().getString(R.string.distance) + ": "; String DEVINFO_CALORY = getContext().getString(R.string.calories) + ": "; - String DEVINFO_HEART = "HR: "; + String DEVINFO_HEART = getContext().getString(R.string.charts_legend_heartrate); String info = ""; if (record.steps > 0) { From 18157daf468a7203e88b86da068abc7a9bc8c86e Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 19 Apr 2017 13:23:13 +0200 Subject: [PATCH 35/40] Ensure that the Notification listener service gets restarted if crashed. This change adds an additional service that checks the status of the NotificationListenerService, and restarts it if it's stale/crashed. Crashes happen mostly during development, but were reported also by users. --- app/src/main/AndroidManifest.xml | 1 + .../gadgetbridge/GBApplication.java | 3 + .../NotificationCollectorMonitorService.java | 79 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/NotificationCollectorMonitorService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b15323fc7..140de6d74 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -251,6 +251,7 @@ + runningServices = manager.getRunningServices(Integer.MAX_VALUE); + if (runningServices == null) { + LOG.info("ensureCollectorRunning() runningServices is NULL"); + return; + } + for (ActivityManager.RunningServiceInfo service : runningServices) { + if (service.service.equals(collectorComponent)) { + LOG.warn("ensureCollectorRunning service - pid: " + service.pid + ", currentPID: " + Process.myPid() + ", clientPackage: " + service.clientPackage + ", clientCount: " + service.clientCount + + ", clientLabel: " + ((service.clientLabel == 0) ? "0" : "(" + getResources().getString(service.clientLabel) + ")")); + if (service.pid == Process.myPid() /*&& service.clientCount > 0 && !TextUtils.isEmpty(service.clientPackage)*/) { + collectorRunning = true; + } + } + } + if (collectorRunning) { + LOG.debug("ensureCollectorRunning: collector is running"); + return; + } + LOG.debug("ensureCollectorRunning: collector not running, reviving..."); + toggleNotificationListenerService(); + } + + private void toggleNotificationListenerService() { + LOG.debug("toggleNotificationListenerService() called"); + ComponentName thisComponent = new ComponentName(this, NotificationListener.class); + PackageManager pm = getPackageManager(); + pm.setComponentEnabledSetting(thisComponent, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + pm.setComponentEnabledSetting(thisComponent, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} \ No newline at end of file From 546b68ad2d2f558e261bbc8b48c4469d1e1b0d35 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 19 Apr 2017 14:52:07 +0200 Subject: [PATCH 36/40] Calendar Sync: detect changed events by hash code --- .../gadgetbridge/daogen/GBDaoGenerator.java | 2 +- .../externalevents/CalendarReceiver.java | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 12c5ff3fc..4820d938b 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -282,7 +282,7 @@ public class GBDaoGenerator { indexUnique.makeUnique(); calendarSyncState.addIndex(indexUnique); calendarSyncState.addToOne(device, deviceId); - calendarSyncState.addIntProperty("syncState").notNull(); + calendarSyncState.addIntProperty("hash").notNull(); } private static Property findProperty(Entity entity, String propertyName) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index 4918e6955..b46ad4141 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -113,8 +113,11 @@ public class CalendarReceiver extends BroadcastReceiver { .build().unique(); if (calendarSyncState == null) { eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED)); - } else { - eventState.put(e.getId(), new EventSyncState(e, calendarSyncState.getSyncState())); + } else if (calendarSyncState.getHash() == e.hashCode()) { + eventState.put(e.getId(), new EventSyncState(e, EventState.NEEDS_UPDATE)); + } + else { + eventState.put(e.getId(), new EventSyncState(e, EventState.SYNCED)); } } } @@ -123,7 +126,7 @@ public class CalendarReceiver extends BroadcastReceiver { List CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list(); for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) { if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) { - eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, CalendarSyncState.getSyncState())); + eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE)); LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName()); } } @@ -137,12 +140,6 @@ public class CalendarReceiver extends BroadcastReceiver { if (es.getState() == EventState.SYNCED) { if (!es.getEvent().equals(eventTable.get(i))) { eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE)); - // update sync status of that Calendar entry in DB for all devices - CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)).build().list(); - for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) { - CalendarSyncState.setSyncState(EventState.NEEDS_UPDATE); - CalendarSyncState.update(); - } } } } else { @@ -160,7 +157,7 @@ public class CalendarReceiver extends BroadcastReceiver { } } catch (Exception e) { e.printStackTrace(); - GB.toast("Datebase Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR); + GB.toast("Database Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR); } } @@ -187,7 +184,7 @@ public class CalendarReceiver extends BroadcastReceiver { es.setState(EventState.SYNCED); eventState.put(i, es); // update db - session.insertOrReplace(new CalendarSyncState(null, deviceId, i, EventState.SYNCED)); + session.insertOrReplace(new CalendarSyncState(null, deviceId, i, es.event.hashCode())); } else if (syncState == EventState.NEEDS_DELETE) { GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); eventState.remove(i); From 3ef5f5b8110cca263e68841296428854dce7c2a1 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 19 Apr 2017 17:44:02 +0200 Subject: [PATCH 37/40] Add support for all day events and add location in the CalendarEventSpec Further: fix the hashCode method to properly deal nulls fields. --- .../externalevents/CalendarReceiver.java | 12 +++++++ .../gadgetbridge/model/CalendarEventSpec.java | 2 ++ .../gadgetbridge/model/CalendarEvents.java | 35 ++++++++++++------- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index b46ad4141..9c4cac6a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -25,7 +25,9 @@ import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Calendar; import java.util.Enumeration; +import java.util.GregorianCalendar; import java.util.Hashtable; import java.util.List; @@ -173,9 +175,19 @@ public class CalendarReceiver extends BroadcastReceiver { CalendarEventSpec calendarEventSpec = new CalendarEventSpec(); calendarEventSpec.id = i; calendarEventSpec.title = calendarEvent.getTitle(); + calendarEventSpec.allDay = calendarEvent.isAllDay(); calendarEventSpec.timestamp = calendarEvent.getBeginSeconds(); //calendarEventSpec.durationInSeconds = calendarEvent.getDurationSeconds(); //FIXME: leads to problems right now + if (calendarEvent.isAllDay()) { + //force the all day events to begin at midnight and last a whole day + Calendar c = GregorianCalendar.getInstance(); + c.setTimeInMillis(calendarEvent.getBegin()); + c.set(Calendar.HOUR, 0); + calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000); + //calendarEventSpec.durationInSeconds = 24 * 60 *60; //TODO: commented because it is commented above + } calendarEventSpec.description = calendarEvent.getDescription(); + calendarEventSpec.location = calendarEvent.getLocation(); calendarEventSpec.type = CalendarEventSpec.TYPE_UNKNOWN; if (syncState == EventState.NEEDS_UPDATE) { GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_UNKNOWN, i); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java index eb1130785..d3035ba29 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java @@ -27,4 +27,6 @@ public class CalendarEventSpec { public int durationInSeconds; public String title; public String description; + public String location; + public boolean allDay; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java index 8172dc138..7be8ee64b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java @@ -21,7 +21,6 @@ import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.provider.CalendarContract; import android.provider.CalendarContract.Instances; import java.util.ArrayList; @@ -48,7 +47,8 @@ public class CalendarEvents { Instances.TITLE, Instances.DESCRIPTION, Instances.EVENT_LOCATION, - Instances.CALENDAR_DISPLAY_NAME + Instances.CALENDAR_DISPLAY_NAME, + Instances.ALL_DAY }; private static final int lookahead_days = 7; @@ -63,16 +63,16 @@ public class CalendarEvents { private boolean fetchSystemEvents(Context mContext) { Calendar cal = GregorianCalendar.getInstance(); - Long dtStart = cal.getTime().getTime(); + Long dtStart = cal.getTimeInMillis(); cal.add(Calendar.DATE, lookahead_days); - Long dtEnd = cal.getTime().getTime(); + Long dtEnd = cal.getTimeInMillis(); - Uri.Builder eventsUriBuilder = CalendarContract.Instances.CONTENT_URI.buildUpon(); + Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon(); ContentUris.appendId(eventsUriBuilder, dtStart); ContentUris.appendId(eventsUriBuilder, dtEnd); Uri eventsUri = eventsUriBuilder.build(); - try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, CalendarContract.Instances.BEGIN + " ASC")) { + try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, Instances.BEGIN + " ASC")) { if (evtCursor == null || evtCursor.getCount() == 0) { return false; } @@ -84,7 +84,8 @@ public class CalendarEvents { evtCursor.getString(4), evtCursor.getString(5), evtCursor.getString(6), - evtCursor.getString(7) + evtCursor.getString(7), + !evtCursor.getString(8).equals("0") ); calendarEventList.add(calEvent); } @@ -100,8 +101,9 @@ public class CalendarEvents { private String description; private String location; private String calName; + private boolean allDay; - public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName) { + public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, boolean allDay) { this.begin = begin; this.end = end; this.id = id; @@ -109,6 +111,7 @@ public class CalendarEvents { this.description = description; this.location = location; this.calName = calName; + this.allDay = allDay; } public long getBegin() { @@ -156,6 +159,10 @@ public class CalendarEvents { return calName; } + public boolean isAllDay() { + return allDay; + } + @Override public boolean equals(Object other) { if (other instanceof CalendarEvent) { @@ -166,7 +173,8 @@ public class CalendarEvents { Objects.equals(this.getLocation(), e.getLocation()) && Objects.equals(this.getDescription(), e.getDescription()) && (this.getEnd() == e.getEnd()) && - Objects.equals(this.getCalName(), e.getCalName()); + Objects.equals(this.getCalName(), e.getCalName()) && + (this.isAllDay() == e.isAllDay()); } else { return false; } @@ -175,12 +183,13 @@ public class CalendarEvents { @Override public int hashCode() { int result = (int) id; - result = 31 * result + title.hashCode(); + result = 31 * result + Objects.hash(title); result = 31 * result + Long.valueOf(begin).hashCode(); - result = 31 * result + location.hashCode(); - result = 31 * result + description.hashCode(); + result = 31 * result + Objects.hash(location); + result = 31 * result + Objects.hash(description); result = 31 * result + Long.valueOf(end).hashCode(); - result = 31 * result + calName.hashCode(); + result = 31 * result + Objects.hash(calName); + result = 31 * result + Boolean.valueOf(allDay).hashCode(); return result; } } From 7ee20348dba94038520e490df3cd058b9d47f3f8 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 19 Apr 2017 21:51:23 +0200 Subject: [PATCH 38/40] Only sync Calendar and Sunrise/Sunset on devices that support it --- .../devices/DeviceCoordinator.java | 7 ++++ .../devices/UnknownDeviceCoordinator.java | 5 +++ .../devices/hplus/HPlusCoordinator.java | 5 +++ .../devices/liveview/LiveviewCoordinator.java | 5 +++ .../devices/miband/MiBandCoordinator.java | 5 +++ .../devices/pebble/PebbleCoordinator.java | 9 ++++- .../vibratissimo/VibratissimoCoordinator.java | 5 +++ .../service/DeviceCommunicationService.java | 38 ++++++++++--------- 8 files changed, 60 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index 515a6e23d..465895acd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -227,4 +227,11 @@ public interface DeviceCoordinator { * @param device */ int getBondingStyle(GBDevice device); + + /** + * Indicates whether the device has some kind of calender we can sync to. + * Also used for generated sunrise/sunset events + */ + boolean supportsCalendarEvents(); + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index 108423d0c..e89198fa4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -176,4 +176,9 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { public Class getAppsManagementActivity() { return null; } + + @Override + public boolean supportsCalendarEvents() { + return false; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 0be66cd6f..f2340dac6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -85,6 +85,11 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return BONDING_STYLE_NONE; } + @Override + public boolean supportsCalendarEvents() { + return false; + } + @Override public DeviceType getDeviceType() { return DeviceType.HPLUS; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java index 4065ee768..5b5c19a7d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java @@ -120,6 +120,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator { return null; } + @Override + public boolean supportsCalendarEvents() { + return false; + } + @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { // nothing to delete, yet diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index e9cd706c9..6afc943b6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -171,6 +171,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return null; } + @Override + public boolean supportsCalendarEvents() { + return false; + } + public static boolean hasValidUserInfo() { String dummyMacAddress = MiBandService.MAC_ADDRESS_FILTER_1_1A + ":00:00:00"; try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index da1358af6..2e4da852b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -20,6 +20,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import android.app.Activity; import android.content.Context; import android.net.Uri; +import android.support.annotation.NonNull; import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -46,6 +47,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { public PebbleCoordinator() { } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); @@ -71,7 +73,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { } @Override - protected void deleteDevice(GBDevice gbDevice, Device device, DaoSession session) throws GBException { + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { Long deviceId = device.getId(); QueryBuilder qb = session.getPebbleHealthActivitySampleDao().queryBuilder(); qb.where(PebbleHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); @@ -154,4 +156,9 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { public Class getAppsManagementActivity() { return AppManagerActivity.class; } + + @Override + public boolean supportsCalendarEvents() { + return true; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java index f86417606..ecc10de17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -120,6 +120,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator { return null; } + @Override + public boolean supportsCalendarEvents() { + return false; + } + @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { // nothing to delete, yet diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 79de077ee..63d442f5f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -41,6 +41,8 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; @@ -206,7 +208,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (mGBDevice.equals(device)) { mGBDevice = device; boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); - setReceiversEnableState(enableReceivers, mGBDevice.isInitialized()); + setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), DeviceHelper.getInstance().getCoordinator(device)); GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context); } else { LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + mGBDevice); @@ -397,7 +399,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere case ACTION_DISCONNECT: { mDeviceSupport.dispose(); if (mGBDevice != null && mGBDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) { - setReceiversEnableState(false, false); + setReceiversEnableState(false, false, null); mGBDevice.setState(GBDevice.State.NOT_CONNECTED); mGBDevice.sendDeviceUpdateIntent(this); } @@ -575,10 +577,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere } - private void setReceiversEnableState(boolean enable, boolean initialized) { + private void setReceiversEnableState(boolean enable, boolean initialized, DeviceCoordinator coordinator) { LOG.info("Setting broadcast receivers to: " + enable); - if (enable && initialized) { + if (enable && initialized && coordinator != null && coordinator.supportsCalendarEvents()) { if (mCalendarReceiver == null) { IntentFilter calendarIntentFilter = new IntentFilter(); calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED"); @@ -587,9 +589,19 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mCalendarReceiver = new CalendarReceiver(mGBDevice); registerReceiver(mCalendarReceiver, calendarIntentFilter); } - } else if (mCalendarReceiver != null) { - unregisterReceiver(mCalendarReceiver); - mCalendarReceiver = null; + if (mAlarmReceiver == null) { + mAlarmReceiver = new AlarmReceiver(); + registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); + } + } else { + if (mCalendarReceiver != null) { + unregisterReceiver(mCalendarReceiver); + mCalendarReceiver = null; + } + if (mAlarmReceiver != null) { + unregisterReceiver(mAlarmReceiver); + mAlarmReceiver = null; + } } if (enable) { @@ -631,11 +643,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mBlueToothPairingRequestReceiver = new BluetoothPairingRequestReceiver(this); registerReceiver(mBlueToothPairingRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)); } - - if (mAlarmReceiver == null) { - mAlarmReceiver = new AlarmReceiver(); - registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); - } if (mAlarmClockReceiver == null) { mAlarmClockReceiver = new AlarmClockReceiver(); IntentFilter filter = new IntentFilter(); @@ -673,11 +680,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mBlueToothPairingRequestReceiver); mBlueToothPairingRequestReceiver = null; } - - if (mAlarmReceiver != null) { - unregisterReceiver(mAlarmReceiver); - mAlarmReceiver = null; - } if (mAlarmClockReceiver != null) { unregisterReceiver(mAlarmClockReceiver); mAlarmClockReceiver = null; @@ -695,7 +697,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); - setReceiversEnableState(false, false); // disable BroadcastReceivers + setReceiversEnableState(false, false, null); // disable BroadcastReceivers setDeviceSupport(null); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); From beb173f162a6759e809f6b9a4d2d0b8f3bb0364b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 19 Apr 2017 21:58:13 +0200 Subject: [PATCH 39/40] change db schema version back to 16 (master is at 15, we will merge now) --- .../freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 4820d938b..4e0c91c3a 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -42,7 +42,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - Schema schema = new Schema(17, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(16, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); From 5717379aec22de8d22135ad9863e7abee2dc1bb2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 20 Apr 2017 10:40:28 +0200 Subject: [PATCH 40/40] Pebble: change timeline layout to "calendar" for calendaar events and display end time --- .../gadgetbridge/externalevents/CalendarReceiver.java | 4 ++-- .../service/devices/pebble/PebbleProtocol.java | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index 9c4cac6a6..2a30556bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -177,14 +177,14 @@ public class CalendarReceiver extends BroadcastReceiver { calendarEventSpec.title = calendarEvent.getTitle(); calendarEventSpec.allDay = calendarEvent.isAllDay(); calendarEventSpec.timestamp = calendarEvent.getBeginSeconds(); - //calendarEventSpec.durationInSeconds = calendarEvent.getDurationSeconds(); //FIXME: leads to problems right now + calendarEventSpec.durationInSeconds = calendarEvent.getDurationSeconds(); //FIXME: leads to problems right now if (calendarEvent.isAllDay()) { //force the all day events to begin at midnight and last a whole day Calendar c = GregorianCalendar.getInstance(); c.setTimeInMillis(calendarEvent.getBegin()); c.set(Calendar.HOUR, 0); calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000); - //calendarEventSpec.durationInSeconds = 24 * 60 *60; //TODO: commented because it is commented above + calendarEventSpec.durationInSeconds = 24 * 60 * 60; //TODO: commented because it is commented above } calendarEventSpec.description = calendarEvent.getDescription(); calendarEventSpec.location = calendarEvent.getLocation(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 582e3878e..c37b9b452 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -525,7 +525,7 @@ public class PebbleProtocol extends GBDeviceProtocol { iconId = PebbleIconID.TIMELINE_CALENDAR; } - return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) calendarEventSpec.durationInSeconds, iconId, calendarEventSpec.title, calendarEventSpec.description); + return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) (calendarEventSpec.durationInSeconds / 60), iconId, calendarEventSpec.title, calendarEventSpec.description); } @Override @@ -841,6 +841,11 @@ public class PebbleProtocol extends GBDeviceProtocol { private byte[] encodeTimelinePin(UUID uuid, int timestamp, short duration, int icon_id, String title, String subtitle) { final short TIMELINE_PIN_LENGTH = 46; + //FIXME: dont depend layout on icon :P + byte layout_id = 0x01; + if (icon_id == PebbleIconID.TIMELINE_CALENDAR) { + layout_id = 0x02; + } icon_id |= 0x80000000; byte attributes_count = 2; byte actions_count = 0; @@ -865,8 +870,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putShort(duration); buf.put((byte) 0x02); // type (0x02 = pin) buf.putShort((short) 0x0001); // flags 0x0001 = ? - buf.put((byte) 0x01); // layout was (0x02 = pin?), 0x01 needed for subtitle but seems to do no harm if there isn't one - + buf.put(layout_id); // layout was (0x02 = pin?), 0x01 needed for subtitle but seems to do no harm if there isn't one buf.putShort((short) attributes_length); // total length of all attributes and actions in bytes buf.put(attributes_count); buf.put(actions_count);