Merge branch 'master' into zetime

This commit is contained in:
Sebastian Kranz
2018-06-29 10:43:11 +02:00
514 changed files with 11625 additions and 1986 deletions
+70 -68
View File
@@ -1,14 +1,13 @@
apply plugin: "com.android.application"
apply plugin: "findbugs"
apply plugin: "pmd"
apply plugin: 'com.android.application'
apply plugin: 'findbugs'
apply plugin: 'pmd'
def ABORT_ON_CHECK_FAILURE=false
def ABORT_ON_CHECK_FAILURE = false
tasks.withType(Test) {
systemProperty 'MiFirmwareDir', System.getProperty('MiFirmwareDir', null)
systemProperty 'logback.configurationFile', System.getProperty('user.dir', null) + '/app/src/main/assets/logback.xml'
systemProperty 'GB_LOGFILES_DIR', java.nio.file.Files.createTempDirectory('gblog').toString();
systemProperty "MiFirmwareDir", System.getProperty("MiFirmwareDir", null)
systemProperty "logback.configurationFile", System.getProperty("user.dir", null) + "/app/src/main/assets/logback.xml"
systemProperty "GB_LOGFILES_DIR", java.nio.file.Files.createTempDirectory("gblog").toString()
}
android {
@@ -17,112 +16,115 @@ android {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
compileSdkVersion 25
buildToolsVersion '26.0.2'
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "nodomain.freeyourgadget.gadgetbridge"
minSdkVersion 19
targetSdkVersion 25
targetSdkVersion 27
// note: always bump BOTH versionCode and versionName!
versionName "0.24.3"
versionCode 120
// Note: always bump BOTH versionCode and versionName!
versionName "0.27.0"
versionCode 132
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
lintOptions {
abortOnError ABORT_ON_CHECK_FAILURE
lintConfig file("${project.rootDir}/config/lint/lint.xml")
// if true, generate an HTML report (with issue explanations, sourcecode, etc)
// If true, generate an HTML report (with issue explanations, sourcecode, etc)
htmlReport true
// optional path to report (default will be lint-results.html in the builddir)
// Optional path to report (default will be lint-results.html in the builddir)
htmlOutput file("$project.buildDir/reports/lint/lint.html")
}
testOptions {
unitTests.returnDefaultValues = true
unitTests {
returnDefaultValues = true
includeAndroidResources = true
}
}
}
pmd {
toolVersion = '5.5.5'
toolVersion = "5.5.5"
}
dependencies {
// testCompile 'ch.qos.logback:logback-classic:1.1.3'
// testCompile 'ch.qos.logback:logback-core:1.1.3'
testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:1.10.19"
testCompile "org.robolectric:robolectric:3.5.1"
// testImplementation "ch.qos.logback:logback-classic:1.1.3"
// testImplementation "ch.qos.logback:logback-core:1.1.3"
testImplementation "junit:junit:4.12"
testImplementation "org.mockito:mockito-core:1.10.19"
testImplementation "org.robolectric:robolectric:3.6.1"
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.4.0'
compile 'com.android.support:cardview-v7:25.4.0'
compile 'com.android.support:recyclerview-v7:25.4.0'
compile 'com.android.support:support-v4:25.4.0'
compile 'com.android.support:gridlayout-v7:25.4.0'
compile 'com.android.support:design:25.4.0'
compile 'com.android.support:palette-v7:25.4.0'
compile('com.github.tony19:logback-android-classic:1.1.1-6') {
exclude group: 'com.google.android', module: 'android'
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:27.1.1"
implementation "com.android.support:cardview-v7:27.1.1"
implementation "com.android.support:recyclerview-v7:27.1.1"
implementation "com.android.support:support-v4:27.1.1"
implementation "com.android.support:gridlayout-v7:27.1.1"
implementation "com.android.support:design:27.1.1"
implementation "com.android.support:palette-v7:27.1.1"
implementation("com.github.tony19:logback-android-classic:1.1.1-6") {
exclude group: "com.google.android", module: "android"
}
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'com.github.Freeyourgadget:MPAndroidChart:5e5bd6c1d3e95c515d4853647ae554e48ee1d593'
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'
implementation "org.slf4j:slf4j-api:1.7.12"
implementation "com.github.Freeyourgadget:MPAndroidChart:5e5bd6c1d3e95c515d4853647ae554e48ee1d593"
implementation "com.github.pfichtner:durationformatter:0.1.1"
implementation "de.cketti.library.changelog:ckchangelog:1.2.2"
implementation "net.e175.klaus:solarpositioning:0.0.9"
// 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 'org.cyanogenmod:platform.sdk:6.0'
implementation "org.greenrobot:greendao:2.2.1"
implementation "org.apache.commons:commons-lang3:3.5"
implementation "org.cyanogenmod:platform.sdk:6.0"
// compile project(":DaoCore")
// implementation project(":DaoCore")
}
preBuild.dependsOn(":GBDaoGenerator:genSources")
gradle.beforeProject {
preBuild.dependsOn(":GBDaoGenerator:genSources")
}
check.dependsOn 'findbugs', 'pmd', 'lint'
check.dependsOn "findbugs", "pmd", "lint"
task pmd(type: Pmd) {
ruleSetFiles = files("${project.rootDir}/config/pmd/pmd-ruleset.xml")
ignoreFailures = !ABORT_ON_CHECK_FAILURE
ruleSets = [
'java-android',
'java-basic',
'java-braces',
'java-clone',
'java-codesize',
'java-controversial',
'java-coupling',
'java-design',
'java-empty',
'java-finalizers',
'java-imports',
'java-junit',
'java-optimizations',
'java-strictexception',
'java-strings',
'java-sunsecure',
'java-typeresolution',
'java-unnecessary',
'java-unusedcode'
"java-android",
"java-basic",
"java-braces",
"java-clone",
"java-codesize",
"java-controversial",
"java-coupling",
"java-design",
"java-empty",
"java-finalizers",
"java-imports",
"java-junit",
"java-optimizations",
"java-strictexception",
"java-strings",
"java-sunsecure",
"java-typeresolution",
"java-unnecessary",
"java-unusedcode"
]
source 'src'
include '**/*.java'
exclude '**/gen/**'
source "src"
include "**/*.java"
exclude "**/gen/**"
reports {
xml.enabled = false
@@ -142,7 +144,7 @@ task findbugs(type: FindBugs) {
reportLevel = "medium"
excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-filter.xml")
classes = files("${project.rootDir}/app/build/intermediates/classes")
source = fileTree('src/main/java/')
source = fileTree("src/main/java/")
classpath = files()
reports {
xml.enabled = false
+8 -2
View File
@@ -17,6 +17,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="cyanogenmod.permission.ACCESS_WEATHER_MANAGER" />
@@ -37,7 +38,8 @@
android:name=".GBApplication"
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/GadgetbridgeTheme">
<activity
@@ -58,6 +60,10 @@
android:name=".devices.miband.MiBandPreferencesActivity"
android:label="@string/preferences_miband_settings"
android:parentActivityName=".activities.SettingsActivity" />
<activity
android:name=".activities.ActivitySummariesActivity"
android:label="@string/activity_summaries"
android:parentActivityName=".activities.ControlCenterv2" />
<activity
android:launchMode="singleTop"
android:name=".activities.appmanager.AppManagerActivity"
@@ -414,7 +420,7 @@
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/screenshot_provider_paths"/>
android:resource="@xml/shared_paths" />
</provider>
<receiver android:name=".SleepAlarmWidget">
@@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Normano64
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Martin, Normano64, Taavi Eomäe
This file is part of Gadgetbridge.
@@ -19,11 +19,17 @@ package nodomain.freeyourgadget.gadgetbridge;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
@@ -52,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothStateChangeReceiver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
@@ -64,6 +71,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
/**
* Main Application class that initializes and provides access to certain things like
* logging and DB access.
@@ -109,6 +118,7 @@ public class GBApplication extends Application {
private static Locale language;
private DeviceManager deviceManager;
private BluetoothStateChangeReceiver bluetoothStateChangeReceiver;
public static void quit() {
GB.log("Quitting Gadgetbridge...", GB.INFO, null);
@@ -166,12 +176,25 @@ public class GBApplication extends Application {
setLanguage(language);
deviceService = createDeviceService();
loadAppsBlackList();
loadAppsNotifBlackList();
loadAppsPebbleBlackList();
loadCalendarsBlackList();
if (isRunningMarshmallowOrLater()) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//the following will ensure the notification manager is kept alive
if(isRunningOreoOrLater()) {
NotificationChannel channel = notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
if(channel == null) {
channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
}
bluetoothStateChangeReceiver = new BluetoothStateChangeReceiver();
registerReceiver(bluetoothStateChangeReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
startService(new Intent(this, NotificationCollectorMonitorService.class));
}
}
@@ -289,6 +312,13 @@ public class GBApplication extends Application {
public static boolean isRunningMarshmallowOrLater() {
return VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
public static boolean isRunningNougatOrLater() {
return VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
public static boolean isRunningOreoOrLater(){
return VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
private static boolean isPrioritySender(int prioritySenders, String number) {
if (prioritySenders == Policy.PRIORITY_SENDERS_ANY) {
@@ -343,58 +373,119 @@ public class GBApplication extends Application {
return NotificationManager.INTERRUPTION_FILTER_ALL;
}
private static HashSet<String> apps_blacklist = null;
private static HashSet<String> apps_notification_blacklist = null;
public static boolean appIsBlacklisted(String packageName) {
if (apps_blacklist == null) {
GB.log("appIsBlacklisted: apps_blacklist is null!", GB.INFO, null);
public static boolean appIsNotifBlacklisted(String packageName) {
if (apps_notification_blacklist == null) {
GB.log("appIsNotifBlacklisted: apps_notification_blacklist is null!", GB.INFO, null);
}
return apps_blacklist != null && apps_blacklist.contains(packageName);
return apps_notification_blacklist != null && apps_notification_blacklist.contains(packageName);
}
public static void setAppsBlackList(Set<String> packageNames) {
public static void setAppsNotifBlackList(Set<String> packageNames) {
if (packageNames == null) {
GB.log("Set null apps_blacklist", GB.INFO, null);
apps_blacklist = new HashSet<>();
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
apps_notification_blacklist = new HashSet<>();
} else {
apps_blacklist = new HashSet<>(packageNames);
apps_notification_blacklist = new HashSet<>(packageNames);
}
GB.log("New apps_blacklist has " + apps_blacklist.size() + " entries", GB.INFO, null);
saveAppsBlackList();
GB.log("New apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
saveAppsNotifBlackList();
}
private static void loadAppsBlackList() {
GB.log("Loading apps_blacklist", GB.INFO, null);
apps_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
if (apps_blacklist == null) {
apps_blacklist = new HashSet<>();
private static void loadAppsNotifBlackList() {
GB.log("Loading apps_notification_blacklist", GB.INFO, null);
apps_notification_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
if (apps_notification_blacklist == null) {
apps_notification_blacklist = new HashSet<>();
}
GB.log("Loaded apps_blacklist has " + apps_blacklist.size() + " entries", GB.INFO, null);
GB.log("Loaded apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
}
private static void saveAppsBlackList() {
GB.log("Saving apps_blacklist with " + apps_blacklist.size() + " entries", GB.INFO, null);
private static void saveAppsNotifBlackList() {
GB.log("Saving apps_notification_blacklist with " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
SharedPreferences.Editor editor = sharedPrefs.edit();
if (apps_blacklist.isEmpty()) {
if (apps_notification_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
} else {
Prefs.putStringSet(editor, GBPrefs.PACKAGE_BLACKLIST, apps_blacklist);
Prefs.putStringSet(editor, GBPrefs.PACKAGE_BLACKLIST, apps_notification_blacklist);
}
editor.apply();
}
public static void addAppToBlacklist(String packageName) {
if (apps_blacklist.add(packageName)) {
saveAppsBlackList();
public static void addAppToNotifBlacklist(String packageName) {
if (apps_notification_blacklist.add(packageName)) {
saveAppsNotifBlackList();
}
}
public static synchronized void removeFromAppsBlacklist(String packageName) {
GB.log("Removing from apps_blacklist: " + packageName, GB.INFO, null);
apps_blacklist.remove(packageName);
saveAppsBlackList();
public static synchronized void removeFromAppsNotifBlacklist(String packageName) {
GB.log("Removing from apps_notification_blacklist: " + packageName, GB.INFO, null);
apps_notification_blacklist.remove(packageName);
saveAppsNotifBlackList();
}
private static HashSet<String> apps_pebblemsg_blacklist = null;
public static boolean appIsPebbleBlacklisted(String sender) {
if (apps_pebblemsg_blacklist == null) {
GB.log("appIsPebbleBlacklisted: apps_pebblemsg_blacklist is null!", GB.INFO, null);
}
return apps_pebblemsg_blacklist != null && apps_pebblemsg_blacklist.contains(sender);
}
public static void setAppsPebbleBlackList(Set<String> packageNames) {
if (packageNames == null) {
GB.log("Set null apps_pebblemsg_blacklist", GB.INFO, null);
apps_pebblemsg_blacklist = new HashSet<>();
} else {
apps_pebblemsg_blacklist = new HashSet<>(packageNames);
}
GB.log("New apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
saveAppsPebbleBlackList();
}
private static void loadAppsPebbleBlackList() {
GB.log("Loading apps_pebblemsg_blacklist", GB.INFO, null);
apps_pebblemsg_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null);
if (apps_pebblemsg_blacklist == null) {
apps_pebblemsg_blacklist = new HashSet<>();
}
GB.log("Loaded apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
}
private static void saveAppsPebbleBlackList() {
GB.log("Saving apps_pebblemsg_blacklist with " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
SharedPreferences.Editor editor = sharedPrefs.edit();
if (apps_pebblemsg_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null);
} else {
Prefs.putStringSet(editor, GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, apps_pebblemsg_blacklist);
}
editor.apply();
}
public static void addAppToPebbleBlacklist(String packageName) {
if (apps_pebblemsg_blacklist.add(packageNameToPebbleMsgSender(packageName))) {
saveAppsPebbleBlackList();
}
}
public static synchronized void removeFromAppsPebbleBlacklist(String packageName) {
GB.log("Removing from apps_pebblemsg_blacklist: " + packageName, GB.INFO, null);
apps_pebblemsg_blacklist.remove(packageNameToPebbleMsgSender(packageName));
saveAppsPebbleBlackList();
}
public static String packageNameToPebbleMsgSender(String packageName) {
if ("eu.siacs.conversations".equals(packageName)){
return("Conversations");
} else if ("net.osmand.plus".equals(packageName)) {
return("OsmAnd");
}
return packageName;
}
private static HashSet<String> calendars_blacklist = null;
public static boolean calendarIsBlacklisted(String calendarDisplayName) {
@@ -406,7 +497,7 @@ public class GBApplication extends Application {
public static void setCalendarsBlackList(Set<String> calendarNames) {
if (calendarNames == null) {
GB.log("Set null apps_blacklist", GB.INFO, null);
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
calendars_blacklist = new HashSet<>();
} else {
calendars_blacklist = new HashSet<>(calendarNames);
@@ -490,7 +581,7 @@ public class GBApplication extends Application {
case 0:
String legacyGender = sharedPrefs.getString("mi_user_gender", null);
String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null);
String legacyWeigth = sharedPrefs.getString("mi_user_weight_kg", null);
String legacyWeight = sharedPrefs.getString("mi_user_weight_kg", null);
String legacyYOB = sharedPrefs.getString("mi_user_year_of_birth", null);
if (legacyGender != null) {
int gender = "male".equals(legacyGender) ? 1 : "female".equals(legacyGender) ? 0 : 2;
@@ -501,8 +592,8 @@ public class GBApplication extends Application {
editor.putString(ActivityUser.PREF_USER_HEIGHT_CM, legacyHeight);
editor.remove("mi_user_height_cm");
}
if (legacyWeigth != null) {
editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeigth);
if (legacyWeight != null) {
editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeight);
editor.remove("mi_user_weight_kg");
}
if (legacyYOB != null) {
@@ -591,4 +682,24 @@ public class GBApplication extends Application {
public static Locale getLanguage() {
return language;
}
public String getVersion() {
try {
return getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA).versionName;
} catch (PackageManager.NameNotFoundException e) {
GB.log("Unable to determine Gadgetbridge's version", GB.WARN, e);
return "0.0.0";
}
}
public String getNameAndVersion() {
try {
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA);
return String.format("%s %s", appInfo.name, packageInfo.versionName);
} catch (PackageManager.NameNotFoundException e) {
GB.log("Unable to determine Gadgetbridge's name/version", GB.WARN, e);
return "Gadgetbridge";
}
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Taavi Eomäe
This file is part of Gadgetbridge.
@@ -24,7 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
/**
* Provides lowlevel access to the database.
* Provides low-level access to the database.
*/
public class LockHandler implements DBHandler {
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer
/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -30,6 +30,7 @@ import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.util.StatusPrinter;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public abstract class Logging {
public static final String PROP_LOGFILES_DIR = "GB_LOGFILES_DIR";
@@ -148,7 +149,7 @@ public abstract class Logging {
}
StringBuilder builder = new StringBuilder(bytes.length * 5);
for (byte b : bytes) {
builder.append(String.format("0x%2x", b));
builder.append(String.format("0x%02x", b));
builder.append(" ");
}
return builder.toString().trim();
@@ -156,9 +157,7 @@ public abstract class Logging {
public static void logBytes(Logger logger, byte[] value) {
if (value != null) {
for (byte b : value) {
logger.warn("DATA: " + String.format("0x%2x", b));
}
logger.warn("DATA: " + GB.hexdump(value, 0, value.length));
}
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Carsten Pfeiffer
/* Copyright (C) 2015-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 0nse, Carsten Pfeiffer
/* Copyright (C) 2016-2018 0nse, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Carsten Pfeiffer
/* Copyright (C) 2015-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, walkjivefly
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -0,0 +1,53 @@
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.os.Bundle;
import android.widget.ListView;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.AbstractItemAdapter;
public abstract class AbstractListActivity<T> extends AbstractGBActivity {
private AbstractItemAdapter<T> itemAdapter;
private ListView itemListView;
public void setItemAdapter(AbstractItemAdapter<T> itemAdapter) {
this.itemAdapter = itemAdapter;
itemListView.setAdapter(itemAdapter);
}
protected void refresh() {
this.itemAdapter.loadItems();
}
public AbstractItemAdapter<T> getItemAdapter() {
return itemAdapter;
}
public ListView getItemListView() {
return itemListView;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
itemListView = findViewById(R.id.itemListView);
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Christian
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Christian
Fischer, Daniele Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -0,0 +1,301 @@
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.app.DatePickerDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.FileProvider;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.DatePicker;
import android.widget.ListView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ActivitySummariesActivity extends AbstractListActivity<BaseActivitySummary> {
private GBDevice mGBDevice;
private SwipeRefreshLayout swipeLayout;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (Objects.requireNonNull(action)) {
case GBDevice.ACTION_DEVICE_CHANGED:
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
mGBDevice = device;
if (device.isBusy()) {
swipeLayout.setRefreshing(true);
} else {
boolean wasBusy = swipeLayout.isRefreshing();
swipeLayout.setRefreshing(false);
if (wasBusy) {
refresh();
}
}
break;
}
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity_list_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean processed = false;
switch (item.getItemId()) {
case R.id.activity_action_manage_timestamp:
resetFetchTimestampToChosenDate();
processed = true;
break;
}
return processed;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
} else {
throw new IllegalArgumentException("Must provide a device when invoking this activity");
}
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
super.onCreate(savedInstanceState);
setItemAdapter(new ActivitySummariesAdapter(this, mGBDevice));
getItemListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Object item = parent.getItemAtPosition(position);
if (item != null) {
ActivitySummary summary = (ActivitySummary) item;
String gpxTrack = summary.getGpxTrack();
if (gpxTrack != null) {
showTrack(gpxTrack);
} else {
GB.toast("This activity does not contain GPX tracks.", Toast.LENGTH_LONG, GB.INFO);
}
}
}
});
getItemListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
getItemListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
@Override
public void onItemCheckedStateChanged(ActionMode actionMode, int position, long id, boolean checked) {
final int selectedItems = getItemListView().getCheckedItemCount();
actionMode.setTitle(selectedItems + " selected");
}
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
getMenuInflater().inflate(R.menu.activity_list_context_menu, menu);
findViewById(R.id.fab).setVisibility(View.INVISIBLE);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
boolean processed = false;
SparseBooleanArray checked = getItemListView().getCheckedItemPositions();
switch (menuItem.getItemId()) {
case R.id.activity_action_delete:
List<BaseActivitySummary> toDelete = new ArrayList<>();
for(int i = 0; i< checked.size(); i++) {
if (checked.valueAt(i)) {
toDelete.add(getItemAdapter().getItem(checked.keyAt(i)));
}
}
deleteItems(toDelete);
processed = true;
break;
case R.id.activity_action_export:
List<String> paths = new ArrayList<>();
for(int i = 0; i< checked.size(); i++) {
if (checked.valueAt(i)) {
BaseActivitySummary item = getItemAdapter().getItem(checked.keyAt(i));
if (item != null) {
ActivitySummary summary = (ActivitySummary) item;
String gpxTrack = summary.getGpxTrack();
if (gpxTrack != null) {
paths.add(gpxTrack);
}
}
}
}
shareMultiple(paths);
processed = true;
break;
case R.id.activity_action_select_all:
for ( int i=0; i < getItemListView().getCount(); i++) {
getItemListView().setItemChecked(i, true);
}
return true; //don't finish actionmode in this case!
default:
break;
}
actionMode.finish();
return processed;
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
findViewById(R.id.fab).setVisibility(View.VISIBLE);
}
});
swipeLayout = findViewById(R.id.list_activity_swipe_layout);
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
fetchTrackData();
}
});
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fetchTrackData();
}
});
}
public void resetFetchTimestampToChosenDate() {
final Calendar currentDate = Calendar.getInstance();
new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Calendar date = Calendar.getInstance();
date.set(year, monthOfYear, dayOfMonth);
Long timestamp = date.getTimeInMillis() - 1000;
SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
editor.remove(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis"); //FIXME: key reconstruction is BAD
editor.putLong(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis", timestamp);
editor.commit();
}
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
}
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
private void deleteItems(List<BaseActivitySummary> items) {
for(BaseActivitySummary item : items) {
item.delete();
getItemAdapter().remove(item);
}
refresh();
}
private void showTrack(String gpxTrack) {
try {
AndroidUtils.viewFile(gpxTrack, Intent.ACTION_VIEW, this);
} catch (IOException e) {
GB.toast(this, "Unable to display GPX track: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
}
}
private void fetchTrackData() {
if (mGBDevice.isInitialized() && !mGBDevice.isBusy()) {
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_GPS_TRACKS);
} else {
swipeLayout.setRefreshing(false);
if (!mGBDevice.isInitialized()) {
GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
}
}
}
private void shareMultiple(List<String> paths){
ArrayList<Uri> uris = new ArrayList<>();
for(String path: paths){
File file = new File(path);
uris.add(FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".screenshot_provider", file));
}
if(uris.size() > 0) {
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setType("application/gpx+xml");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
startActivity(Intent.createChooser(intent, "SHARE"));
} else {
GB.toast(this, "No selected activity contains a GPX track to share", Toast.LENGTH_SHORT, GB.ERROR);
}
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -22,6 +22,8 @@ import android.support.v4.app.NavUtils;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import org.slf4j.Logger;
@@ -64,12 +66,25 @@ public class AppBlacklistActivity extends AbstractGBActivity {
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.app_blacklist_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
case R.id.blacklist_all_notif:
appBlacklistAdapter.blacklistAllNotif();
return true;
case R.id.whitelist_all_notif:
appBlacklistAdapter.whitelistAllNotif();
return true;
}
return super.onOptionsItemSelected(item);
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,5 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Taavi Eomäe
This file is part of Gadgetbridge.
@@ -24,7 +24,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -42,15 +41,13 @@ import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import de.cketti.library.changelog.ChangeLog;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -72,9 +69,7 @@ public class ControlCenterv2 extends AppCompatActivity
}
private DeviceManager deviceManager;
private ImageView background;
private List<GBDevice> deviceList;
private GBDeviceAdapterv2 mGBDeviceAdapter;
private RecyclerView deviceListView;
@@ -84,7 +79,7 @@ public class ControlCenterv2 extends AppCompatActivity
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
switch (Objects.requireNonNull(action)) {
case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage(), true);
break;
@@ -104,10 +99,10 @@ public class ControlCenterv2 extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_controlcenterv2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -115,28 +110,29 @@ public class ControlCenterv2 extends AppCompatActivity
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.controlcenter_navigation_drawer_open, R.string.controlcenter_navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
//end of material design boilerplate
deviceManager = ((GBApplication) getApplication()).getDeviceManager();
deviceListView = (RecyclerView) findViewById(R.id.deviceListView);
deviceListView = findViewById(R.id.deviceListView);
deviceListView.setHasFixedSize(true);
deviceListView.setLayoutManager(new LinearLayoutManager(this));
background = (ImageView) findViewById(R.id.no_items_bg);
deviceList = deviceManager.getDevices();
List<GBDevice> deviceList = deviceManager.getDevices();
mGBDeviceAdapter = new GBDeviceAdapterv2(this, deviceList);
deviceListView.setAdapter(this.mGBDeviceAdapter);
/* uncomment to enable fixed-swipe to reveal more actions
ItemTouchHelper swipeToDismissTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.LEFT , ItemTouchHelper.RIGHT) {
@Override
@@ -167,9 +163,8 @@ public class ControlCenterv2 extends AppCompatActivity
}
});
//uncomment to enable fixed-swipe to reveal more actions
//swipeToDismissTouchHelper.attachToRecyclerView(deviceListView);
swipeToDismissTouchHelper.attachToRecyclerView(deviceListView);
*/
registerForContextMenu(deviceListView);
@@ -226,7 +221,7 @@ public class ControlCenterv2 extends AppCompatActivity
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
@@ -237,7 +232,7 @@ public class ControlCenterv2 extends AppCompatActivity
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
switch (item.getItemId()) {
@@ -253,6 +248,10 @@ public class ControlCenterv2 extends AppCompatActivity
Intent dbIntent = new Intent(this, DbManagementActivity.class);
startActivity(dbIntent);
return true;
case R.id.action_blacklist:
Intent blIntent = new Intent(this, AppBlacklistActivity.class);
startActivity(blIntent);
return true;
case R.id.action_quit:
GBApplication.quit();
return true;
@@ -283,13 +282,6 @@ public class ControlCenterv2 extends AppCompatActivity
}
private void refreshPairedDevices() {
List<GBDevice> deviceList = deviceManager.getDevices();
if (deviceList.isEmpty()) {
background.setVisibility(View.VISIBLE);
} else {
background.setVisibility(View.INVISIBLE);
}
mGBDeviceAdapter.notifyDataSetChanged();
}
@@ -309,6 +301,8 @@ public class ControlCenterv2 extends AppCompatActivity
wantedPermissions.add(Manifest.permission.READ_PHONE_STATE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.RECEIVE_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_SMS);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_DENIED)
@@ -317,6 +311,11 @@ public class ControlCenterv2 extends AppCompatActivity
wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.READ_CALENDAR);
try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MEDIA_CONTENT_CONTROL) == PackageManager.PERMISSION_DENIED)
wantedPermissions.add(Manifest.permission.MEDIA_CONTENT_CONTROL);
} catch (Exception ignored){
}
if (!wantedPermissions.isEmpty())
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0);
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2016-2018 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Frank Slezak, ivanovlev, Kasha, Lem Dulfo, Steffen Liebergeld
This file is part of Gadgetbridge.
@@ -40,18 +40,21 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
public class DebugActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
@@ -61,23 +64,12 @@ public class DebugActivity extends AbstractGBActivity {
= "nodomain.freeyourgadget.gadgetbridge.DebugActivity.action.reply";
private Spinner sendTypeSpinner;
private Button sendButton;
private Button incomingCallButton;
private Button outgoingCallButton;
private Button startCallButton;
private Button endCallButton;
private Button testNotificationButton;
private Button setMusicInfoButton;
private Button setTimeButton;
private Button rebootButton;
private Button HeartRateButton;
private Button testNewFunctionalityButton;
private EditText editContent;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
switch (Objects.requireNonNull(intent.getAction())) {
case ACTION_REPLY: {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
CharSequence reply = remoteInput.getCharSequence(EXTRA_REPLY);
@@ -85,11 +77,9 @@ public class DebugActivity extends AbstractGBActivity {
GB.toast(context, "got wearable reply: " + reply, Toast.LENGTH_SHORT, GB.INFO);
break;
}
case DeviceService.ACTION_HEARTRATE_MEASUREMENT: {
int hrValue = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, -1);
GB.toast(DebugActivity.this, "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO);
default:
LOG.info("ignoring intent action " + intent.getAction());
break;
}
}
}
};
@@ -105,17 +95,17 @@ public class DebugActivity extends AbstractGBActivity {
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
registerReceiver(mReceiver, filter); // for ACTION_REPLY
editContent = (EditText) findViewById(R.id.editContent);
editContent = findViewById(R.id.editContent);
ArrayList<String> spinnerArray = new ArrayList<>();
for (NotificationType notificationType : NotificationType.values()) {
spinnerArray.add(notificationType.name());
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, spinnerArray);
sendTypeSpinner = (Spinner) findViewById(R.id.sendTypeSpinner);
sendTypeSpinner = findViewById(R.id.sendTypeSpinner);
sendTypeSpinner.setAdapter(spinnerArrayAdapter);
sendButton = (Button) findViewById(R.id.sendButton);
Button sendButton = findViewById(R.id.sendButton);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -132,7 +122,7 @@ public class DebugActivity extends AbstractGBActivity {
}
});
incomingCallButton = (Button) findViewById(R.id.incomingCallButton);
Button incomingCallButton = findViewById(R.id.incomingCallButton);
incomingCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -142,7 +132,7 @@ public class DebugActivity extends AbstractGBActivity {
GBApplication.deviceService().onSetCallState(callSpec);
}
});
outgoingCallButton = (Button) findViewById(R.id.outgoingCallButton);
Button outgoingCallButton = findViewById(R.id.outgoingCallButton);
outgoingCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -153,7 +143,7 @@ public class DebugActivity extends AbstractGBActivity {
}
});
startCallButton = (Button) findViewById(R.id.startCallButton);
Button startCallButton = findViewById(R.id.startCallButton);
startCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -162,7 +152,7 @@ public class DebugActivity extends AbstractGBActivity {
GBApplication.deviceService().onSetCallState(callSpec);
}
});
endCallButton = (Button) findViewById(R.id.endCallButton);
Button endCallButton = findViewById(R.id.endCallButton);
endCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -172,15 +162,15 @@ public class DebugActivity extends AbstractGBActivity {
}
});
rebootButton = (Button) findViewById(R.id.rebootButton);
Button rebootButton = findViewById(R.id.rebootButton);
rebootButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GBApplication.deviceService().onReboot();
}
});
HeartRateButton = (Button) findViewById(R.id.HearRateButton);
HeartRateButton.setOnClickListener(new View.OnClickListener() {
Button heartRateButton = findViewById(R.id.HeartRateButton);
heartRateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GB.toast("Measuring heart rate, please wait...", Toast.LENGTH_LONG, GB.INFO);
@@ -188,7 +178,7 @@ public class DebugActivity extends AbstractGBActivity {
}
});
setMusicInfoButton = (Button) findViewById(R.id.setMusicInfoButton);
Button setMusicInfoButton = findViewById(R.id.setMusicInfoButton);
setMusicInfoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -214,7 +204,7 @@ public class DebugActivity extends AbstractGBActivity {
}
});
setTimeButton = (Button) findViewById(R.id.setTimeButton);
Button setTimeButton = findViewById(R.id.setTimeButton);
setTimeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -222,7 +212,7 @@ public class DebugActivity extends AbstractGBActivity {
}
});
testNotificationButton = (Button) findViewById(R.id.testNotificationButton);
Button testNotificationButton = findViewById(R.id.testNotificationButton);
testNotificationButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -230,7 +220,15 @@ public class DebugActivity extends AbstractGBActivity {
}
});
testNewFunctionalityButton = (Button) findViewById(R.id.testNewFunctionality);
Button fetchDebugLogsButton = findViewById(R.id.fetchDebugLogsButton);
fetchDebugLogsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_DEBUGLOGS);
}
});
Button testNewFunctionalityButton = findViewById(R.id.testNewFunctionality);
testNewFunctionalityButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -266,7 +264,7 @@ public class DebugActivity extends AbstractGBActivity {
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender().addAction(action);
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this)
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.test_notification))
.setContentText(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
.setTicker(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
@@ -275,7 +273,9 @@ public class DebugActivity extends AbstractGBActivity {
.setContentIntent(pendingIntent)
.extend(wearableExtender);
nManager.notify((int) System.currentTimeMillis(), ncomp.build());
if (nManager != null) {
nManager.notify((int) System.currentTimeMillis(), ncomp.build());
}
}
@Override
@@ -295,7 +295,4 @@ public class DebugActivity extends AbstractGBActivity {
unregisterReceiver(mReceiver);
}
public interface DeviceSelectionCallback {
void invoke(GBDevice device);
}
}
@@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, JohnnySun, Lem Dulfo, Uwe Hermann
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, JohnnySun, Lem Dulfo, Taavi Eomäe, Uwe Hermann
This file is part of Gadgetbridge.
@@ -24,6 +24,7 @@ import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
@@ -54,6 +55,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -66,8 +68,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY;
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener {
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
@@ -80,7 +80,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
switch (Objects.requireNonNull(intent.getAction())) {
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
if (isScanning != Scanning.SCANNING_BTLE && isScanning != Scanning.SCANNING_NEW_BTLE) {
discoveryStarted(Scanning.SCANNING_BT);
@@ -105,9 +105,8 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
});
break;
case BluetoothAdapter.ACTION_STATE_CHANGED:
int oldState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF);
int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
bluetoothStateChanged(oldState, newState);
bluetoothStateChanged(newState);
break;
case BluetoothDevice.ACTION_FOUND: {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -119,7 +118,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
ParcelUuid[] uuids2 = AndroidUtils.toParcelUUids(uuids);
ParcelUuid[] uuids2 = AndroidUtils.toParcelUuids(uuids);
handleDeviceFound(device, rssi, uuids2);
break;
}
@@ -265,7 +264,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_discovery);
startButton = (Button) findViewById(R.id.discovery_start);
startButton = findViewById(R.id.discovery_start);
startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -273,11 +272,11 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
}
});
progressView = (ProgressBar) findViewById(R.id.discovery_progressbar);
progressView = findViewById(R.id.discovery_progressbar);
progressView.setProgress(0);
progressView.setIndeterminate(true);
progressView.setVisibility(View.GONE);
ListView deviceCandidatesView = (ListView) findViewById(R.id.discovery_deviceCandidatesView);
ListView deviceCandidatesView = findViewById(R.id.discovery_deviceCandidatesView);
cadidateListAdapter = new DeviceCandidateAdapter(this, deviceCandidates);
deviceCandidatesView.setAdapter(cadidateListAdapter);
@@ -443,10 +442,15 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void stopNewBTLEDiscovery() {
adapter.getBluetoothLeScanner().stopScan(newLeScanCallback);
BluetoothLeScanner bluetoothLeScanner = adapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null) {
LOG.warn("could not get BluetoothLeScanner()!");
return;
}
bluetoothLeScanner.stopScan(newLeScanCallback);
}
private void bluetoothStateChanged(int oldState, int newState) {
private void bluetoothStateChanged(int newState) {
discoveryFinished();
if (newState == BluetoothAdapter.STATE_ON) {
this.adapter = BluetoothAdapter.getDefaultAdapter();
@@ -530,12 +534,12 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
private ScanSettings getScanSettings() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return new ScanSettings.Builder()
.setScanMode(SCAN_MODE_LOW_LATENCY)
.setMatchMode(MATCH_MODE_STICKY)
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
.setMatchMode(android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY)
.build();
} else {
return new ScanSettings.Builder()
.setScanMode(SCAN_MODE_LOW_LATENCY)
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
}
}
@@ -611,4 +615,14 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
}
}
}
@Override
protected void onPause() {
super.onPause();
stopBTDiscovery();
stopBTLEDiscovery();
if (GB.supportsBluetoothLE()) {
stopNewBTLEDiscovery();
}
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo, Uwe Hermann
This file is part of Gadgetbridge.
@@ -34,19 +34,26 @@ import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.GBChromeClient;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.GBWebClient;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.JSInterface;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
public class ExternalPebbleJSActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class);
@@ -64,24 +71,69 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
boolean showConfig = false;
UUID currentUUID = null;
GBDevice currentDevice = null;
if (extras == null) {
throw new IllegalArgumentException("Must provide device and uuid in extras when invoking this activity");
}
confUri = getIntent().getData();
if(confUri.getScheme().equals("gadgetbridge")) {
try {
currentUUID = UUID.fromString(confUri.getHost());
} catch (IllegalArgumentException e) {
LOG.error("UUID in incoming configuration is not a valid UUID: " +confUri.toString());
}
if (extras.getBoolean(START_BG_WEBVIEW, false)) {
startBackgroundWebViewAndFinish();
return;
}
//first check if we are still connected to a pebble
DeviceManager deviceManager = ((GBApplication) getApplication()).getDeviceManager();
List<GBDevice> deviceList = deviceManager.getDevices();
for (GBDevice device : deviceList) {
if (device.getState() == GBDevice.State.INITIALIZED) {
if (device.getType().equals(DeviceType.PEBBLE)) {
currentDevice = device;
break;
} else {
LOG.error("attempting to load pebble configuration but a different device type is connected!!!");
finish();
return;
}
}
}
if (currentDevice == null) {
//then try to reconnect to last connected device
String btDeviceAddress = GBApplication.getPrefs().getPreferences().getString("last_device_address", null);
if (btDeviceAddress != null) {
GBDevice candidate = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this);
if(!candidate.isConnected() && candidate.getType() == DeviceType.PEBBLE){
Intent intent = new Intent(this, DeviceCommunicationService.class)
.setAction(ACTION_CONNECT)
.putExtra(GBDevice.EXTRA_DEVICE, currentDevice);
this.startService(intent);
currentDevice = candidate;
}
}
}
GBDevice currentDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
UUID currentUUID = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID);
showConfig = true; //we are getting incoming configuration data
}
} else {
currentDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
currentUUID = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID);
if (extras.getBoolean(START_BG_WEBVIEW, false)) {
startBackgroundWebViewAndFinish();
return;
}
showConfig = extras.getBoolean(SHOW_CONFIG, false);
}
if (GBApplication.getGBPrefs().isBackgroundJsEnabled()) {
if (extras.getBoolean(SHOW_CONFIG, false)) {
if (showConfig) {
Objects.requireNonNull(currentDevice, "Must provide a device when invoking this activity");
Objects.requireNonNull(currentUUID, "Must provide a uuid when invoking this activity");
WebViewSingleton.runJavascriptInterface(currentDevice, currentUUID);
WebViewSingleton.getInstance().runJavascriptInterface(this, currentDevice, currentUUID);
}
// FIXME: is this really supposed to be outside the check for SHOW_CONFIG?
@@ -104,7 +156,7 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
private void setupBGWebView() {
setContentView(R.layout.activity_external_pebble_js);
myWebView = WebViewSingleton.getWebView(this);
myWebView = WebViewSingleton.getInstance().getWebView(this);
if (myWebView.getParent() != null) {
((ViewGroup) myWebView.getParent()).removeView(myWebView);
}
@@ -161,6 +213,7 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
} catch (IllegalArgumentException e) {
GB.toast("returned uri: " + confUri.toString(), Toast.LENGTH_LONG, GB.ERROR);
}
myWebView.stopLoading();
myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString);
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Andreas Shimokawa
/* Copyright (C) 2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer
/* Copyright (C) 2016-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -27,4 +27,8 @@ public class HeartRateUtils {
* Value is in minutes
*/
public static final int MAX_HR_MEASUREMENTS_GAP_MINUTES = 10;
public static boolean isValidHeartRateValue(int value) {
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti, Normano64
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti, Felix Konstantin Maurer, Normano64
This file is part of Gadgetbridge.
@@ -18,7 +18,6 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.Manifest;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -29,16 +28,13 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
@@ -47,7 +43,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -65,6 +60,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DISPLAY_ITEMS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_SLEEP_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_STEPS_GOAL;
@@ -330,6 +326,20 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
});
final Preference displayPages = findPreference("bip_display_items");
displayPages.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
}
});
return true;
}
});
// Get all receivers of Media Buttons
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
@@ -377,7 +387,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
/*
Either returns the file path of the selected document, or the display name
Either returns the file path of the selected document, or the display name, or an empty string
*/
private String getAutoExportLocationSummary() {
String autoExportLocation = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null);
@@ -386,20 +396,21 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
Uri uri = Uri.parse(autoExportLocation);
try {
String filePath = AndroidUtils.getFilePath(getApplicationContext(), uri);
if (filePath != null) {
return filePath;
return AndroidUtils.getFilePath(getApplicationContext(), uri);
} catch (IllegalArgumentException e) {
try {
Cursor cursor = getContentResolver().query(
uri,
new String[]{DocumentsContract.Document.COLUMN_DISPLAY_NAME},
null, null, null, null
);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME));
}
}
catch (Exception fdfsdfds) {
LOG.warn("fuck");
}
} catch (URISyntaxException e) {
return "";
}
Cursor cursor = getContentResolver().query(
uri,
new String[] { DocumentsContract.Document.COLUMN_DISPLAY_NAME },
null, null, null, null
);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME));
}
return "";
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -428,7 +428,7 @@ public abstract class AbstractAppManagerFragment extends Fragment {
startActivity(startIntent);
return true;
case R.id.appmanager_app_openinstore:
String url = "https://apps.getpebble.com/en_US/search/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/1?query=" + selectedApp.getName() + "&dev_settings=true";
String url = "https://pebble-appstore.romanport.com/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/0/?query=" + selectedApp.getName();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa
/* Copyright (C) 2016-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa
/* Copyright (C) 2016-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti, walkjivefly
This file is part of Gadgetbridge.
@@ -70,6 +70,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import static nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils.isValidHeartRateValue;
/**
* A base class fragment to be used with ChartsActivity. The fragment can supply
* a title to be displayed in the activity by returning non-null in #getTitle()
@@ -439,6 +441,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
List<Entry> notWornEntries = new ArrayList<>(numEntries);
boolean hr = supportsHeartrate(gbDevice);
List<Entry> heartrateEntries = hr ? new ArrayList<Entry>(numEntries) : null;
List<Integer> colors = new ArrayList<>(numEntries); // this is kinda inefficient...
int lastHrSampleIndex = -1;
for (int i = 0; i < numEntries; i++) {
@@ -509,7 +512,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
}
activityEntries.add(createLineEntry(value, ts));
}
if (hr && isValidHeartRateValue(sample.getHeartRate())) {
if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && HeartRateUtils.isValidHeartRateValue(sample.getHeartRate())) {
if (lastHrSampleIndex > -1 && ts - lastHrSampleIndex > 1800*HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) {
heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1));
heartrateEntries.add(createLineEntry(0, ts - 1));
@@ -574,10 +577,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
return new DefaultChartsData(lineData, xValueFormatter);
}
protected boolean isValidHeartRateValue(int value) {
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
}
/**
* Implement this to supply the samples to be displayed.
*
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Alberto, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2018 0nse, Alberto, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Vebryn
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Vebryn
This file is part of Gadgetbridge.
@@ -36,15 +36,12 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -52,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdap
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -59,16 +57,11 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
public class ChartsActivity extends AbstractGBFragmentActivity implements ChartsHost {
private static final Logger LOG = LoggerFactory.getLogger(ChartsActivity.class);
private Button mPrevButton;
private Button mNextButton;
private TextView mDateControl;
private Date mStartDate;
private Date mEndDate;
private SwipeRefreshLayout swipeLayout;
private NonSwipeableViewPager viewPager;
LimitedQueue mActivityAmountCache = new LimitedQueue(60);
@@ -86,7 +79,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_charts_durationdialog);
durationLabel = (TextView) findViewById(R.id.charts_duration_label);
durationLabel = findViewById(R.id.charts_duration_label);
setDuration(mDuration);
}
@@ -103,7 +96,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
switch (Objects.requireNonNull(action)) {
case GBDevice.ACTION_DEVICE_CHANGED:
GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
refreshBusyState(dev);
@@ -145,7 +138,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
throw new IllegalArgumentException("Must provide a device when invoking this activity");
}
swipeLayout = (SwipeRefreshLayout) findViewById(R.id.activity_swipe_layout);
swipeLayout = findViewById(R.id.activity_swipe_layout);
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
@@ -155,7 +148,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
enableSwipeRefresh(true);
// Set up the ViewPager with the sections adapter.
viewPager = (NonSwipeableViewPager) findViewById(R.id.charts_pager);
NonSwipeableViewPager viewPager = findViewById(R.id.charts_pager);
viewPager.setAdapter(getPagerAdapter());
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
@@ -172,8 +165,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
}
});
dateBar = (ViewGroup) findViewById(R.id.charts_date_bar);
mDateControl = (TextView) findViewById(R.id.charts_text_date);
dateBar = findViewById(R.id.charts_date_bar);
mDateControl = findViewById(R.id.charts_text_date);
mDateControl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -182,22 +175,20 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
}
});
mPrevButton = (Button) findViewById(R.id.charts_previous);
Button mPrevButton = findViewById(R.id.charts_previous);
mPrevButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handlePrevButtonClicked();
}
});
mNextButton = (Button) findViewById(R.id.charts_next);
Button mNextButton = findViewById(R.id.charts_next);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleNextButtonClicked();
}
});
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.charts_main_layout);
}
private String formatDetailedDuration() {
@@ -284,7 +275,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
private void fetchActivityData() {
if (getDevice().isInitialized()) {
GBApplication.deviceService().onFetchActivityData();
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
} else {
swipeLayout.setRefreshing(false);
GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer
/* Copyright (C) 2016-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -168,7 +168,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
private void addSample(ActivitySample sample) {
int heartRate = sample.getHeartRate();
int timestamp = tsTranslation.shorten(sample.getTimestamp());
if (isValidHeartRateValue(heartRate)) {
if (HeartRateUtils.isValidHeartRateValue(heartRate)) {
setCurrentHeartRate(heartRate, timestamp);
}
int steps = sample.getSteps();
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Carsten Pfeiffer
/* Copyright (C) 2015-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti, Vebryn
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer
/* Copyright (C) 2016-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer
/* Copyright (C) 2016-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -0,0 +1,122 @@
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
import android.support.annotation.DrawableRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
/**
* Adapter for displaying generic ItemWithDetails instances.
*/
public abstract class AbstractItemAdapter<T> extends ArrayAdapter<T> {
public static final int SIZE_SMALL = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE = 3;
private final Context context;
private final List<T> items;
private boolean horizontalAlignment;
private int size = SIZE_MEDIUM;
public AbstractItemAdapter(Context context) {
this (context, new ArrayList<T>());
}
public AbstractItemAdapter(Context context, List<T> items) {
super(context, 0, items);
this.context = context;
this.items = items;
}
public void setHorizontalAlignment(boolean horizontalAlignment) {
this.horizontalAlignment = horizontalAlignment;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
T item = getItem(position);
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (horizontalAlignment) {
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
} else {
switch (size) {
case SIZE_SMALL:
view = inflater.inflate(R.layout.item_with_details_small, parent, false);
break;
default:
view = inflater.inflate(R.layout.item_with_details, parent, false);
break;
}
}
}
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
TextView nameView = (TextView) view.findViewById(R.id.item_name);
TextView detailsView = (TextView) view.findViewById(R.id.item_details);
nameView.setText(getName(item));
detailsView.setText(getDetails(item));
iconView.setImageResource(getIcon(item));
return view;
}
protected abstract String getName(T item);
protected abstract String getDetails(T item);
@DrawableRes
protected abstract int getIcon(T item);
public void setSize(int size) {
this.size = size;
}
public int getSize() {
return size;
}
public List<T> getItems() {
return items;
}
public void loadItems() {
}
public void setItems(List<T> items, boolean notify) {
this.items.clear();
this.items.addAll(items);
if (notify) {
notifyDataSetChanged();
}
}
}
@@ -0,0 +1,84 @@
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
import android.widget.Toast;
import java.util.Date;
import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ActivitySummariesAdapter extends AbstractItemAdapter<BaseActivitySummary> {
private final GBDevice device;
public ActivitySummariesAdapter(Context context, GBDevice device) {
super(context);
this.device = device;
loadItems();
}
@Override
public void loadItems() {
try (DBHandler handler = GBApplication.acquireDB()) {
BaseActivitySummaryDao summaryDao = handler.getDaoSession().getBaseActivitySummaryDao();
Device dbDevice = DBHelper.findDevice(device, handler.getDaoSession());
QueryBuilder<BaseActivitySummary> qb = summaryDao.queryBuilder();
qb.where(BaseActivitySummaryDao.Properties.DeviceId.eq(dbDevice.getId())).orderDesc(BaseActivitySummaryDao.Properties.StartTime);
List<BaseActivitySummary> allSummaries = qb.build().list();
setItems(allSummaries, true);
} catch (Exception e) {
GB.toast("Error loading activity summaries.", Toast.LENGTH_SHORT, GB.ERROR, e);
}
}
@Override
protected String getName(BaseActivitySummary item) {
String name = item.getName();
if (name != null && name.length() > 0) {
return name;
}
Date startTime = item.getStartTime();
if (startTime != null) {
return DateTimeUtils.formatDateTime(startTime);
}
return "Unknown activity";
}
@Override
protected String getDetails(BaseActivitySummary item) {
return ActivityKind.asString(item.getActivityKind(), getContext());
}
@Override
protected int getIcon(BaseActivitySummary item) {
return ActivityKind.getIconId(item.getActivityKind());
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -23,7 +23,7 @@ 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.CheckedTextView;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
@@ -32,12 +32,16 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.packageNameToPebbleMsgSender;
public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapter.AppBLViewHolder> implements Filterable {
private List<ApplicationInfo> applicationInfoList;
@@ -62,7 +66,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
if (name == null) {
name = ai.packageName;
}
if (GBApplication.appIsBlacklisted(ai.packageName)) {
if (GBApplication.appIsNotifBlacklisted(ai.packageName) || GBApplication.appIsPebbleBlacklisted(packageNameToPebbleMsgSender(ai.packageName))) {
// sort blacklisted first by prefixing with a '!'
name = "!" + name;
}
@@ -94,23 +98,52 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
holder.deviceAppNameLabel.setText(mNameMap.get(appInfo));
holder.deviceImageView.setImageDrawable(appInfo.loadIcon(mPm));
holder.checkbox.setChecked(GBApplication.appIsBlacklisted(appInfo.packageName));
holder.blacklist_checkbox.setChecked(GBApplication.appIsNotifBlacklisted(appInfo.packageName));
holder.blacklist_pebble_checkbox.setChecked(GBApplication.appIsPebbleBlacklisted(packageNameToPebbleMsgSender(appInfo.packageName)));
holder.blacklist_pebble_checkbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
((CheckedTextView) view).toggle();
if ( ((CheckedTextView)view).isChecked() ) {
GBApplication.addAppToPebbleBlacklist(appInfo.packageName);
} else {
GBApplication.removeFromAppsPebbleBlacklist(appInfo.packageName);
}
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox));
CheckedTextView checkBox = ((CheckedTextView) v.findViewById(R.id.item_checkbox));
checkBox.toggle();
if (checkBox.isChecked()) {
GBApplication.addAppToBlacklist(appInfo.packageName);
GBApplication.addAppToNotifBlacklist(appInfo.packageName);
} else {
GBApplication.removeFromAppsBlacklist(appInfo.packageName);
GBApplication.removeFromAppsNotifBlacklist(appInfo.packageName);
}
}
});
}
public void blacklistAllNotif() {
Set<String> apps_blacklist = new HashSet<>();
List<ApplicationInfo> allApps = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo ai : allApps) {
apps_blacklist.add(ai.packageName);
}
GBApplication.setAppsNotifBlackList(apps_blacklist);
notifyDataSetChanged();
}
public void whitelistAllNotif() {
Set<String> apps_blacklist = new HashSet<>();
GBApplication.setAppsNotifBlackList(apps_blacklist);
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return applicationInfoList.size();
@@ -123,9 +156,10 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
return applicationFilter;
}
public class AppBLViewHolder extends RecyclerView.ViewHolder {
class AppBLViewHolder extends RecyclerView.ViewHolder {
final CheckBox checkbox;
final CheckedTextView blacklist_checkbox;
final CheckedTextView blacklist_pebble_checkbox;
final ImageView deviceImageView;
final TextView deviceAppVersionAuthorLabel;
final TextView deviceAppNameLabel;
@@ -133,7 +167,8 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
AppBLViewHolder(View itemView) {
super(itemView);
checkbox = (CheckBox) itemView.findViewById(R.id.item_checkbox);
blacklist_checkbox = (CheckedTextView) itemView.findViewById(R.id.item_checkbox);
blacklist_pebble_checkbox = (CheckedTextView) itemView.findViewById(R.id.item_pebble_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);
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Lem Dulfo
This file is part of Gadgetbridge.
@@ -22,6 +22,7 @@ import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.CardView;
@@ -43,6 +44,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
@@ -51,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -69,16 +72,16 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
this.deviceList = deviceList;
}
@NonNull
@Override
public GBDeviceAdapterv2.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
public GBDeviceAdapterv2.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
this.parent = parent;
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_itemv2, parent, false);
ViewHolder vh = new ViewHolder(view);
return vh;
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
final GBDevice device = deviceList.get(position);
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
@@ -143,7 +146,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
GBApplication.deviceService().onFetchActivityData();
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
}
}
);
@@ -210,6 +213,20 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
}
);
//show activity tracks
holder.showActivityTracks.setVisibility(coordinator.supportsActivityTracks() ? View.VISIBLE : View.GONE);
holder.showActivityTracks.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ActivitySummariesActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
ItemWithDetailsAdapter infoAdapter = new ItemWithDetailsAdapter(context, device.getDeviceInfos());
infoAdapter.setHorizontalAlignment(true);
holder.deviceInfoList.setAdapter(infoAdapter);
@@ -338,6 +355,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
ImageView manageAppsView;
ImageView setAlarmsView;
ImageView showActivityGraphs;
ImageView showActivityTracks;
ImageView deviceInfoView;
//overflow
@@ -348,44 +366,44 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
ViewHolder(View view) {
super(view);
container = (CardView) view.findViewById(R.id.card_view);
container = view.findViewById(R.id.card_view);
deviceImageView = (ImageView) view.findViewById(R.id.device_image);
deviceNameLabel = (TextView) view.findViewById(R.id.device_name);
deviceStatusLabel = (TextView) view.findViewById(R.id.device_status);
deviceImageView = view.findViewById(R.id.device_image);
deviceNameLabel = view.findViewById(R.id.device_name);
deviceStatusLabel = view.findViewById(R.id.device_status);
//actions
batteryStatusBox = (LinearLayout) view.findViewById(R.id.device_battery_status_box);
batteryStatusLabel = (TextView) view.findViewById(R.id.battery_status);
batteryIcon = (ImageView) view.findViewById(R.id.device_battery_status);
fetchActivityDataBox = (LinearLayout) view.findViewById(R.id.device_action_fetch_activity_box);
fetchActivityData = (ImageView) view.findViewById(R.id.device_action_fetch_activity);
busyIndicator = (ProgressBar) view.findViewById(R.id.device_busy_indicator);
takeScreenshotView = (ImageView) view.findViewById(R.id.device_action_take_screenshot);
manageAppsView = (ImageView) view.findViewById(R.id.device_action_manage_apps);
setAlarmsView = (ImageView) view.findViewById(R.id.device_action_set_alarms);
showActivityGraphs = (ImageView) view.findViewById(R.id.device_action_show_activity_graphs);
deviceInfoView = (ImageView) view.findViewById(R.id.device_info_image);
batteryStatusBox = view.findViewById(R.id.device_battery_status_box);
batteryStatusLabel = view.findViewById(R.id.battery_status);
batteryIcon = view.findViewById(R.id.device_battery_status);
fetchActivityDataBox = view.findViewById(R.id.device_action_fetch_activity_box);
fetchActivityData = view.findViewById(R.id.device_action_fetch_activity);
busyIndicator = view.findViewById(R.id.device_busy_indicator);
takeScreenshotView = view.findViewById(R.id.device_action_take_screenshot);
manageAppsView = view.findViewById(R.id.device_action_manage_apps);
setAlarmsView = view.findViewById(R.id.device_action_set_alarms);
showActivityGraphs = view.findViewById(R.id.device_action_show_activity_graphs);
showActivityTracks = view.findViewById(R.id.device_action_show_activity_tracks);
deviceInfoView = view.findViewById(R.id.device_info_image);
deviceInfoBox = (RelativeLayout) view.findViewById(R.id.device_item_infos_box);
deviceInfoBox = view.findViewById(R.id.device_item_infos_box);
//overflow
deviceInfoList = (ListView) view.findViewById(R.id.device_item_infos);
findDevice = (ImageView) view.findViewById(R.id.device_action_find);
removeDevice = (ImageView) view.findViewById(R.id.device_action_remove);
deviceInfoList = view.findViewById(R.id.device_item_infos);
findDevice = view.findViewById(R.id.device_action_find);
removeDevice = view.findViewById(R.id.device_action_remove);
}
}
public void justifyListViewHeightBasedOnChildren(ListView listView) {
private void justifyListViewHeightBasedOnChildren(ListView listView) {
ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
if (adapter == null) {
return;
}
ViewGroup vg = listView;
int totalHeight = 0;
for (int i = 0; i < adapter.getCount(); i++) {
View listItem = adapter.getView(i, null, vg);
View listItem = adapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
@@ -427,11 +445,11 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
private void showTransientSnackbar(int resource) {
Snackbar snackbar = Snackbar.make(parent, resource, Snackbar.LENGTH_SHORT);
View snackbarView = snackbar.getView();
//View snackbarView = snackbar.getView();
// change snackbar text color
int snackbarTextId = android.support.design.R.id.snackbar_text;
TextView textView = (TextView) snackbarView.findViewById(snackbarTextId);
// change snackbar text color
//int snackbarTextId = android.support.design.R.id.snackbar_text;
//TextView textView = snackbarView.findViewById(snackbarTextId);
//textView.setTextColor();
//snackbarView.setBackgroundColor(Color.MAGENTA);
snackbar.show();
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -17,77 +17,33 @@
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
/**
* Adapter for displaying generic ItemWithDetails instances.
*/
public class ItemWithDetailsAdapter extends ArrayAdapter<ItemWithDetails> {
public static final int SIZE_SMALL = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE = 3;
private final Context context;
private boolean horizontalAlignment;
private int size = SIZE_MEDIUM;
public class ItemWithDetailsAdapter extends AbstractItemAdapter<ItemWithDetails> {
public ItemWithDetailsAdapter(Context context, List<ItemWithDetails> items) {
super(context, 0, items);
this.context = context;
}
public void setHorizontalAlignment(boolean horizontalAlignment) {
this.horizontalAlignment = horizontalAlignment;
super(context, items);
}
@Override
public View getView(int position, View view, ViewGroup parent) {
ItemWithDetails item = getItem(position);
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (horizontalAlignment) {
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
} else {
switch (size) {
case SIZE_SMALL:
view = inflater.inflate(R.layout.item_with_details_small, parent, false);
break;
default:
view = inflater.inflate(R.layout.item_with_details, parent, false);
break;
}
}
}
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
TextView nameView = (TextView) view.findViewById(R.id.item_name);
TextView detailsView = (TextView) view.findViewById(R.id.item_details);
nameView.setText(item.getName());
detailsView.setText(item.getDetails());
iconView.setImageResource(item.getIcon());
return view;
protected String getName(ItemWithDetails item) {
return item.getName();
}
public void setSize(int size) {
this.size = size;
@Override
protected String getDetails(ItemWithDetails item) {
return item.getDetails();
}
public int getSize() {
return size;
@Override
protected int getIcon(ItemWithDetails item) {
return item.getIcon();
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, JohnnySun
This file is part of Gadgetbridge.
@@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, JohnnySun
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Felix Konstantin Maurer, JohnnySun
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,3 +1,19 @@
/* Copyright (C) 2018 Carsten Pfeiffer, Felix Konstantin Maurer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.database;
import android.app.AlarmManager;
@@ -65,8 +81,9 @@ public class PeriodicExporter extends BroadcastReceiver {
return;
}
Uri dstUri = Uri.parse(dst);
OutputStream out = context.getContentResolver().openOutputStream(dstUri);
helper.exportDB(dbHandler, out);
try (OutputStream out = context.getContentResolver().openOutputStream(dstUri)) {
helper.exportDB(dbHandler, out);
}
} catch (Exception ex) {
GB.updateExportFailedNotification(context.getString(R.string.notif_export_failed_title), context);
LOG.info("Exception while exporting DB: ", ex);
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa
/* Copyright (C) 2016-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa
/* Copyright (C) 2016-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 protomors
/* Copyright (C) 2017-2018 protomors
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa
/* Copyright (C) 2015-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Andreas Shimokawa
/* Copyright (C) 2017-2018 Andreas Shimokawa
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Carsten Pfeiffer, Daniele Gobbetti
/* Copyright (C) 2015-2018 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
@@ -124,4 +124,9 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
public int getBondingStyle(GBDevice device) {
return BONDING_STYLE_ASK;
}
@Override
public boolean supportsActivityTracks() {
return false;
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, JohnnySun, Uwe Hermann
This file is part of Gadgetbridge.
@@ -139,6 +139,14 @@ public interface DeviceCoordinator {
*/
boolean supportsActivityTracking();
/**
* Indicates whether the device supports recording dedicated activity tracks, like
* walking, hiking, running, swimming, etc. and retrieving the recorded
* data. This is different from the constant activity tracking since the tracks are
* usually recorded with additional features, like e.g. GPS.
*/
boolean supportsActivityTracks();
/**
* Returns true if activity data fetching is supported AND possible at this
* very moment. This will consider the device state (being connected/disconnected/busy...)
@@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -39,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
* Provides access to the list of devices managed by Gadgetbridge.
@@ -149,6 +151,8 @@ public class DeviceManager {
}
}
}
GB.updateNotification(selectedDevice, context);
}
private void refreshPairedDevices() {
@@ -163,7 +167,10 @@ public class DeviceManager {
Collections.sort(deviceList, new Comparator<GBDevice>() {
@Override
public int compare(GBDevice lhs, GBDevice rhs) {
return Collator.getInstance().compare(lhs.getName(), rhs.getName());
if (rhs.getStateOrdinal() - lhs.getStateOrdinal() == 0) {
return Collator.getInstance().compare(lhs.getName(), rhs.getName());
}
return (rhs.getStateOrdinal() - lhs.getStateOrdinal());
}
});
notifyDevicesChanged();
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Julien Pivotto, Kasha, Steffen Liebergeld, Uwe Hermann
This file is part of Gadgetbridge.
@@ -67,7 +67,7 @@ public interface EventHandler {
void onAppReorder(UUID uuids[]);
void onFetchActivityData();
void onFetchRecordedData(int dataTypes);
void onReboot();
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, João Paulo Barraca
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
@@ -0,0 +1,222 @@
/* Copyright (C) 2017-2018 Carsten Pfeiffer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip;
import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class BipActivitySummary extends BaseActivitySummary {
private int version;
private float distanceMeters;
private float ascentMeters;
private float descentMeters;
private float minAltitude;
private float maxAltitude;
private int minLatitude;
private int maxLatitude;
private int minLongitude;
private int maxLongitude;
private long steps;
private long activeTimeSeconds;
private float caloriesBurnt;
private float maxSpeed;
private float minPace;
private float maxPace;
private float totalStride;
private long timeAscent;
private long timeDescent;
private long timeFlat;
private int averageHR;
private int averagePace;
private int averageStride;
// @Override
// public long getSteps() {
// return steps;
// }
//
// @Override
// public float getDistanceMeters() {
// return distanceMeters;
// }
//
// @Override
// public float getAscentMeters() {
// return ascentMeters;
// }
//
// @Override
// public float getDescentMeters() {
// return descentMeters;
// }
//
// @Override
// public float getMinAltitude() {
// return minAltitude;
// }
//
// @Override
// public float getMaxAltitude() {
// return maxAltitude;
// }
//
// @Override
// public float getCalories() {
// return caloriesBurnt;
// }
//
// @Override
// public float getMaxSpeed() {
// return maxSpeed;
// }
//
// @Override
// public float getMinSpeed() {
// return minPace;
// }
//
// @Override
// public float getAverageSpeed() {
// return averagePace;
// }
public void setVersion(int version) {
this.version = version;
}
public int getVersion() {
return version;
}
public void setDistanceMeters(float distanceMeters) {
this.distanceMeters = distanceMeters;
}
public void setAscentMeters(float ascentMeters) {
this.ascentMeters = ascentMeters;
}
public void setDescentMeters(float descentMeters) {
this.descentMeters = descentMeters;
}
public void setMinAltitude(float minAltitude) {
this.minAltitude = minAltitude;
}
public void setMaxAltitude(float maxAltitude) {
this.maxAltitude = maxAltitude;
}
public void setMinLatitude(int minLatitude) {
this.minLatitude = minLatitude;
}
public void setMaxLatitude(int maxLatitude) {
this.maxLatitude = maxLatitude;
}
public void setMinLongitude(int minLongitude) {
this.minLongitude = minLongitude;
}
public void setMaxLongitude(int maxLongitude) {
this.maxLongitude = maxLongitude;
}
public void setSteps(long steps) {
this.steps = steps;
}
public void setActiveTimeSeconds(long activeTimeSeconds) {
this.activeTimeSeconds = activeTimeSeconds;
}
public void setCaloriesBurnt(float caloriesBurnt) {
this.caloriesBurnt = caloriesBurnt;
}
public void setMaxSpeed(float maxSpeed) {
this.maxSpeed = maxSpeed;
}
public void setMinPace(float minPace) {
this.minPace = minPace;
}
public void setMaxPace(float maxPace) {
this.maxPace = maxPace;
}
public void setTotalStride(float totalStride) {
this.totalStride = totalStride;
}
public float getTotalStride() {
return totalStride;
}
public void setTimeAscent(long timeAscent) {
this.timeAscent = timeAscent;
}
public long getTimeAscent() {
return timeAscent;
}
public void setTimeDescent(long timeDescent) {
this.timeDescent = timeDescent;
}
public long getTimeDescent() {
return timeDescent;
}
public void setTimeFlat(long timeFlat) {
this.timeFlat = timeFlat;
}
public long getTimeFlat() {
return timeFlat;
}
public void setAverageHR(int averageHR) {
this.averageHR = averageHR;
}
public int getAverageHR() {
return averageHR;
}
public void setAveragePace(int averagePace) {
this.averagePace = averagePace;
}
public int getAveragePace() {
return averagePace;
}
public void setAverageStride(int averageStride) {
this.averageStride = averageStride;
}
public int getAverageStride() {
return averageStride;
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 João Paulo Barraca, Quallenauge
/* Copyright (C) 2017-2018 João Paulo Barraca, Quallenauge
This file is part of Gadgetbridge.
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, João
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, João
Paulo Barraca
This file is part of Gadgetbridge.

Some files were not shown because too many files have changed in this diff Show More