Merge branch 'master' into db-refactoring
|
@ -8,7 +8,7 @@ tasks.withType(Test) { systemProperty 'MiFirmwareDir', System.getProperty('MiFir
|
|||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "nodomain.freeyourgadget.gadgetbridge"
|
||||
|
@ -46,12 +46,12 @@ dependencies {
|
|||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'com.android.support:appcompat-v7:23.3.0'
|
||||
compile 'com.android.support:support-v4:23.3.0'
|
||||
compile 'com.android.support:design:23.3.0'
|
||||
compile 'com.github.tony19:logback-android-classic:1.1.1-4'
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
compile 'com.github.PhilJay:MPAndroidChart:v2.2.3'
|
||||
compile 'com.github.PhilJay:MPAndroidChart:v2.2.4'
|
||||
compile 'com.github.pfichtner:durationformatter:0.1.1'
|
||||
compile 'de.cketti.library.changelog:ckchangelog:1.2.2'
|
||||
compile 'de.greenrobot:greendao:2.1.0'
|
||||
|
|
|
@ -196,10 +196,6 @@ public class GBApplication extends Application {
|
|||
dbLock.unlock();
|
||||
}
|
||||
|
||||
public static boolean isRunningOnKitkatOrLater() {
|
||||
return VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
}
|
||||
|
||||
public static boolean isRunningLollipopOrLater() {
|
||||
return VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
|
@ -299,10 +295,14 @@ public class GBApplication extends Application {
|
|||
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
|
||||
break;
|
||||
}
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static LimitedQueue getIDSenderLookup() {
|
||||
return mIDSenderLookup;
|
||||
}
|
||||
|
||||
public static boolean isDarkThemeEnabled() {
|
||||
return sharedPrefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_light)).equals(context.getString(R.string.pref_theme_value_dark));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
|
||||
|
@ -18,7 +17,7 @@ import android.support.v4.app.FragmentPagerAdapter;
|
|||
*
|
||||
* @see AbstractGBFragment
|
||||
*/
|
||||
public abstract class AbstractGBFragmentActivity extends FragmentActivity {
|
||||
public abstract class AbstractGBFragmentActivity extends GBActivity {
|
||||
/**
|
||||
* The {@link android.support.v4.view.PagerAdapter} that will provide
|
||||
* fragments for each of the sections. We use a
|
||||
|
|
|
@ -20,6 +20,9 @@ import android.view.ViewGroup;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
/**
|
||||
* A settings activity with support for preferences directly displaying their value.
|
||||
* If you combine such preferences with a custom OnPreferenceChangeListener, you have
|
||||
|
@ -86,6 +89,11 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity {
|
|||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (GBApplication.isDarkThemeEnabled()) {
|
||||
setTheme(R.style.GadgetbridgeThemeDark);
|
||||
} else {
|
||||
setTheme(R.style.GadgetbridgeTheme);
|
||||
}
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
|
@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.CheckBox;
|
||||
|
@ -12,7 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
|
||||
|
||||
public class AlarmDetails extends AppCompatActivity {
|
||||
public class AlarmDetails extends GBActivity {
|
||||
|
||||
private GBAlarm alarm;
|
||||
private TimePicker timePicker;
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public class AndroidPairingActivity extends AppCompatActivity {
|
||||
public class AndroidPairingActivity extends GBActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -28,13 +27,14 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
|
||||
public class AppBlacklistActivity extends AppCompatActivity {
|
||||
public class AppBlacklistActivity extends GBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class);
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
|
@ -60,6 +60,29 @@ public class AppBlacklistActivity extends AppCompatActivity {
|
|||
final List<ApplicationInfo> packageList = pm.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
ListView appListView = (ListView) findViewById(R.id.appListView);
|
||||
|
||||
// sort the package list by label and blacklist status
|
||||
final IdentityHashMap<ApplicationInfo, String> nameMap = new IdentityHashMap<>(packageList.size());
|
||||
for (ApplicationInfo ai : packageList) {
|
||||
CharSequence name = pm.getApplicationLabel(ai);
|
||||
if (name == null) {
|
||||
name = ai.packageName;
|
||||
}
|
||||
if (GBApplication.blacklist.contains(ai.packageName)) {
|
||||
// sort blacklisted first by prefixing with a '!'
|
||||
name = "!" + name;
|
||||
}
|
||||
nameMap.put(ai, name.toString());
|
||||
}
|
||||
|
||||
Collections.sort(packageList, new Comparator<ApplicationInfo>() {
|
||||
@Override
|
||||
public int compare(ApplicationInfo ai1, ApplicationInfo ai2) {
|
||||
final String s1 = nameMap.get(ai1);
|
||||
final String s2 = nameMap.get(ai2);
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
});
|
||||
|
||||
final ArrayAdapter<ApplicationInfo> adapter = new ArrayAdapter<ApplicationInfo>(this, R.layout.item_with_checkbox, packageList) {
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
|
@ -80,22 +103,6 @@ public class AppBlacklistActivity extends AppCompatActivity {
|
|||
|
||||
checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName));
|
||||
|
||||
Collections.sort(packageList, new Comparator<ApplicationInfo>() {
|
||||
@Override
|
||||
public int compare(ApplicationInfo ai1, ApplicationInfo ai2) {
|
||||
boolean blacklisted1 = GBApplication.blacklist.contains(ai1.packageName);
|
||||
boolean blacklisted2 = GBApplication.blacklist.contains(ai2.packageName);
|
||||
|
||||
if ((blacklisted1 && blacklisted2) || (!blacklisted1 && !blacklisted2)) {
|
||||
// both blacklisted or both not blacklisted = sort by alphabet
|
||||
return ai1.packageName.compareTo(ai2.packageName);
|
||||
} else if (blacklisted1) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -37,7 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||
|
||||
|
||||
public class AppManagerActivity extends AppCompatActivity {
|
||||
public class AppManagerActivity extends GBActivity {
|
||||
public static final String ACTION_REFRESH_APPLIST
|
||||
= "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppManagerActivity.class);
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ListView;
|
||||
|
||||
|
@ -21,7 +20,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
|
|||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS;
|
||||
|
||||
|
||||
public class ConfigureAlarms extends AppCompatActivity {
|
||||
public class ConfigureAlarms extends GBActivity {
|
||||
|
||||
private static final int REQ_CONFIGURE_ALARM = 1;
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import android.support.v4.app.ActivityCompat;
|
|||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
@ -48,7 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class ControlCenter extends AppCompatActivity {
|
||||
public class ControlCenter extends GBActivity {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ControlCenter.class);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.os.Bundle;
|
|||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.RemoteInput;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
@ -37,7 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
|
||||
public class DebugActivity extends AppCompatActivity {
|
||||
public class DebugActivity extends GBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
|
||||
|
||||
private static final String EXTRA_REPLY = "reply";
|
||||
|
|
|
@ -12,7 +12,6 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
|
@ -33,7 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class DiscoveryActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
||||
public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemClickListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
||||
private static final long SCAN_DURATION = 60000; // 60s
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Intent;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.ConsoleMessage;
|
||||
|
@ -32,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||
|
||||
public class ExternalPebbleJSActivity extends AppCompatActivity {
|
||||
public class ExternalPebbleJSActivity extends GBActivity {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class);
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
@ -35,7 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
|
||||
public class FwAppInstallerActivity extends AppCompatActivity implements InstallActivity {
|
||||
public class FwAppInstallerActivity extends GBActivity implements InstallActivity {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class);
|
||||
private static final String ITEM_DETAILS = "details";
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
|
||||
public class GBActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (GBApplication.isDarkThemeEnabled()) {
|
||||
setTheme(R.style.GadgetbridgeThemeDark);
|
||||
} else {
|
||||
setTheme(R.style.GadgetbridgeTheme);
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
public class HeartRateUtils {
|
||||
public static final int MAX_HEART_RATE_VALUE = 250;
|
||||
public static final int MIN_HEART_RATE_VALUE = 0;
|
||||
/**
|
||||
* The maxiumum gap between two hr measurements in which
|
||||
* we interpolate between the measurements. Otherwise, two
|
||||
* distinct measurements will be shown.
|
||||
*
|
||||
* Value is in minutes
|
||||
*/
|
||||
public static final int MAX_HR_MEASUREMENTS_GAP_MINUTES = 10;
|
||||
}
|
|
@ -133,11 +133,6 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
|||
@Override
|
||||
protected String[] getPreferenceKeysWithSummary() {
|
||||
return new String[]{
|
||||
"audio_player",
|
||||
"notification_mode_calls",
|
||||
"notification_mode_sms",
|
||||
"notification_mode_k9mail",
|
||||
"pebble_activitytracker",
|
||||
"pebble_emu_addr",
|
||||
"pebble_emu_port",
|
||||
"pebble_reconnect_attempts",
|
||||
|
@ -159,7 +154,6 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
|||
"canned_reply_15",
|
||||
"canned_reply_16",
|
||||
PREF_USER_YEAR_OF_BIRTH,
|
||||
PREF_USER_GENDER,
|
||||
PREF_USER_HEIGHT_CM,
|
||||
PREF_USER_WEIGHT_KG,
|
||||
PREF_USER_SLEEP_DURATION,
|
||||
|
|
|
@ -4,11 +4,13 @@ import android.content.BroadcastReceiver;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
|
@ -39,6 +41,7 @@ import java.util.Set;
|
|||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
|
@ -116,6 +119,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
protected int CHART_TEXT_COLOR;
|
||||
protected int LEGEND_TEXT_COLOR;
|
||||
protected int HEARTRATE_COLOR;
|
||||
protected int HEARTRATE_FILL_COLOR;
|
||||
protected int AK_ACTIVITY_COLOR;
|
||||
protected int AK_DEEP_SLEEP_COLOR;
|
||||
protected int AK_LIGHT_SLEEP_COLOR;
|
||||
|
@ -147,11 +151,16 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
}
|
||||
|
||||
protected void init() {
|
||||
BACKGROUND_COLOR = getResources().getColor(R.color.background_material_light);
|
||||
DESCRIPTION_COLOR = getResources().getColor(R.color.primarytext);
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = getContext().getTheme();
|
||||
theme.resolveAttribute(android.R.attr.background, typedValue, true);
|
||||
BACKGROUND_COLOR = typedValue.data;
|
||||
theme.resolveAttribute(android.R.attr.textColor, typedValue, true);
|
||||
LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = typedValue.data;
|
||||
|
||||
CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext);
|
||||
LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext);
|
||||
HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate);
|
||||
HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill);
|
||||
AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light);
|
||||
AK_DEEP_SLEEP_COLOR = getResources().getColor(R.color.chart_light_sleep_light);
|
||||
AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light);
|
||||
|
@ -335,6 +344,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
}
|
||||
|
||||
protected void configureChartDefaults(Chart<?> chart) {
|
||||
chart.setDescription("");
|
||||
|
||||
// if enabled, the chart will always start at zero on the y-axis
|
||||
chart.setNoDataText(getString(R.string.chart_no_data_synchronize));
|
||||
|
||||
|
@ -343,6 +354,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
|
||||
// enable touch gestures
|
||||
chart.setTouchEnabled(true);
|
||||
|
||||
setupLegend(chart);
|
||||
}
|
||||
|
||||
protected void configureBarLineChartDefaults(BarLineChartBase<?> chart) {
|
||||
|
@ -380,9 +393,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
/**
|
||||
* This method reads the data from the database, analyzes and prepares it for
|
||||
* the charts. This will be called from a background task, so there must not be
|
||||
* any UI access. #renderCharts will be automatically called after this method.
|
||||
* any UI access. #updateChartsInUIThread and #renderCharts will be automatically called after this method.
|
||||
*/
|
||||
protected abstract void refreshInBackground(DBHandler db, GBDevice device);
|
||||
protected abstract ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device);
|
||||
|
||||
/**
|
||||
* Triggers the actual (re-) rendering of the chart.
|
||||
|
@ -390,7 +403,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
*/
|
||||
protected abstract void renderCharts();
|
||||
|
||||
protected void refresh(GBDevice gbDevice, BarLineChartBase chart, List<ActivitySample> samples) {
|
||||
protected DefaultChartsData refresh(GBDevice gbDevice, List<ActivitySample> samples) {
|
||||
Calendar cal = GregorianCalendar.getInstance();
|
||||
cal.clear();
|
||||
Date date;
|
||||
|
@ -398,6 +411,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
String dateStringTo = "";
|
||||
|
||||
LOG.info("" + getTitle() + ": number of samples:" + samples.size());
|
||||
CombinedData combinedData;
|
||||
if (samples.size() > 1) {
|
||||
boolean annotate = true;
|
||||
boolean use_steps_as_movement;
|
||||
|
@ -413,6 +427,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
boolean hr = supportsHeartrate();
|
||||
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++) {
|
||||
ActivitySample sample = samples.get(i);
|
||||
|
@ -455,7 +470,13 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
}
|
||||
activityEntries.add(createBarEntry(value, i));
|
||||
if (hr && isValidHeartRateValue(sample.getCustomValue())) {
|
||||
if (lastHrSampleIndex > -1 && i - lastHrSampleIndex > HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) {
|
||||
heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1));
|
||||
heartrateEntries.add(createLineEntry(0, i - 1));
|
||||
}
|
||||
|
||||
heartrateEntries.add(createLineEntry(sample.getCustomValue(), i));
|
||||
lastHrSampleIndex = i;
|
||||
}
|
||||
|
||||
String xLabel = "";
|
||||
|
@ -486,11 +507,11 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
xLabels.add(xLabel);
|
||||
}
|
||||
|
||||
chart.getXAxis().setValues(xLabels);
|
||||
// chart.getXAxis().setValues(xLabels);
|
||||
|
||||
BarDataSet activitySet = createActivitySet(activityEntries, colors, "Activity");
|
||||
// create a data object with the datasets
|
||||
CombinedData combinedData = new CombinedData(xLabels);
|
||||
combinedData = new CombinedData(xLabels);
|
||||
List<IBarDataSet> list = new ArrayList<>();
|
||||
list.add(activitySet);
|
||||
BarData barData = new BarData(xLabels, list);
|
||||
|
@ -503,21 +524,17 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
combinedData.setData(lineData);
|
||||
}
|
||||
|
||||
chart.setDescription("");
|
||||
// chart.setDescription(getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo));
|
||||
// chart.setDescriptionPosition(?, ?);
|
||||
|
||||
setupLegend(chart);
|
||||
|
||||
chart.setData(combinedData);
|
||||
} else {
|
||||
CombinedData data = new CombinedData(Collections.<String>emptyList());
|
||||
chart.setData(data);
|
||||
combinedData = new CombinedData(Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
return new DefaultChartsData(combinedData);
|
||||
}
|
||||
|
||||
protected boolean isValidHeartRateValue(int value) {
|
||||
return value > 0 && value < 255;
|
||||
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,23 +578,19 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
|
||||
protected LineDataSet createHeartrateSet(List<Entry> values, String label) {
|
||||
LineDataSet set1 = new LineDataSet(values, label);
|
||||
set1.setLineWidth(0.8f);
|
||||
set1.setColor(HEARTRATE_COLOR);
|
||||
// set1.setColors(colors);
|
||||
set1.setDrawCubic(true);
|
||||
set1.setCubicIntensity(0.1f);
|
||||
// //set1.setDrawFilled(true);
|
||||
// set1.setDrawCircles(false);
|
||||
// set1.setLineWidth(2f);
|
||||
|
||||
set1.setDrawCircles(false);
|
||||
// set1.setCircleRadius(2f);
|
||||
// set1.setDrawFilled(true);
|
||||
|
||||
set1.setLineWidth(0.8f);
|
||||
// set1.setColor(getResources().getColor(android.R.color.background_light));
|
||||
// set1.setCircleColor(HEARTRATE_COLOR);
|
||||
// set1.setFillColor(ColorTemplate.getHoloBlue());
|
||||
set1.setDrawValues(true);
|
||||
// set1.setHighLightColor(Color.rgb(128, 0, 255));
|
||||
// set1.setColor(Color.rgb(89, 178, 44));
|
||||
set1.setDrawValues(true);
|
||||
set1.setValueTextColor(CHART_TEXT_COLOR);
|
||||
set1.setAxisDependency(YAxis.AxisDependency.RIGHT);
|
||||
return set1;
|
||||
|
@ -622,6 +635,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
}
|
||||
|
||||
public class RefreshTask extends DBAccess {
|
||||
private ChartsData chartsData;
|
||||
|
||||
public RefreshTask(String task, Context context) {
|
||||
super(task, context);
|
||||
}
|
||||
|
@ -630,7 +645,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
protected void doInBackground(DBHandler db) {
|
||||
ChartsHost chartsHost = getChartsHost();
|
||||
if (chartsHost != null) {
|
||||
refreshInBackground(db, chartsHost.getDevice());
|
||||
chartsData = refreshInBackground(chartsHost, db, chartsHost.getDevice());
|
||||
} else {
|
||||
cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,6 +656,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
super.onPostExecute(o);
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
|
||||
updateChartsnUIThread(chartsData);
|
||||
renderCharts();
|
||||
} else {
|
||||
LOG.info("Not rendering charts because activity is not available anymore");
|
||||
|
@ -646,6 +664,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract void updateChartsnUIThread(ChartsData chartsData);
|
||||
|
||||
/**
|
||||
* Returns true if the date was successfully shifted, and false if the shift
|
||||
* was ignored, e.g. when the to-value is in the future.
|
||||
|
@ -689,4 +709,16 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||
private int toTimestamp(Date date) {
|
||||
return (int) ((date.getTime() / 1000));
|
||||
}
|
||||
|
||||
public static class DefaultChartsData extends ChartsData {
|
||||
private final CombinedData combinedData;
|
||||
|
||||
public DefaultChartsData(CombinedData combinedData) {
|
||||
this.combinedData = combinedData;
|
||||
}
|
||||
|
||||
public CombinedData getCombinedData() {
|
||||
return combinedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
@ -83,8 +84,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
|||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(250);
|
||||
yAxisRight.setAxisMinValue(0);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
|
||||
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
||||
refresh();
|
||||
|
@ -106,11 +107,16 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db, GBDevice device) {
|
||||
protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||
List<ActivitySample> samples = getSamples(db, device);
|
||||
refresh(device, mChart, samples);
|
||||
return refresh(device, samples);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChartsnUIThread(ChartsData chartsData) {
|
||||
DefaultChartsData dcd = (DefaultChartsData) chartsData;
|
||||
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
mChart.setData(dcd.getCombinedData());
|
||||
}
|
||||
|
||||
protected void renderCharts() {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||
|
||||
public abstract class ChartsData {
|
||||
}
|
|
@ -23,6 +23,7 @@ import com.github.mikephil.charting.components.YAxis;
|
|||
import com.github.mikephil.charting.data.BarData;
|
||||
import com.github.mikephil.charting.data.BarDataSet;
|
||||
import com.github.mikephil.charting.data.BarEntry;
|
||||
import com.github.mikephil.charting.data.ChartData;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.data.LineData;
|
||||
import com.github.mikephil.charting.data.LineDataSet;
|
||||
|
@ -39,10 +40,12 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Measurement;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class LiveActivityFragment extends AbstractChartFragment {
|
||||
|
@ -63,6 +66,9 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
private final Steps mSteps = new Steps();
|
||||
private ScheduledExecutorService pulseScheduler;
|
||||
private int maxStepsResetCounter;
|
||||
private List<Measurement> heartRateValues;
|
||||
private LineDataSet mHeartRateSet;
|
||||
private int mHeartRate;
|
||||
|
||||
private class Steps {
|
||||
private int initialSteps;
|
||||
|
@ -145,16 +151,36 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
case DeviceService.ACTION_REALTIME_STEPS:
|
||||
case DeviceService.ACTION_REALTIME_STEPS: {
|
||||
int steps = intent.getIntExtra(DeviceService.EXTRA_REALTIME_STEPS, 0);
|
||||
long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
refreshCurrentSteps(steps, timestamp);
|
||||
addEntries(steps, timestamp);
|
||||
break;
|
||||
}
|
||||
case DeviceService.ACTION_HEARTRATE_MEASUREMENT: {
|
||||
int heartRate = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, 0);
|
||||
long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
if (isValidHeartRateValue(heartRate)) {
|
||||
setCurrentHeartRate(heartRate, timestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void refreshCurrentSteps(int steps, long timestamp) {
|
||||
private void setCurrentHeartRate(int heartRate, long timestamp) {
|
||||
addHistoryDataSet(true);
|
||||
mHeartRate = heartRate;
|
||||
}
|
||||
|
||||
private int getCurrentHeartRate() {
|
||||
int result = mHeartRate;
|
||||
mHeartRate = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addEntries(int steps, long timestamp) {
|
||||
mSteps.updateCurrentSteps(steps, timestamp);
|
||||
if (++maxStepsResetCounter > RESET_COUNT) {
|
||||
maxStepsResetCounter = 0;
|
||||
|
@ -163,10 +189,10 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
// Or: count down the steps until goal reached? And then flash GOAL REACHED -> Set stretch goal
|
||||
LOG.info("Steps: " + steps + ", total: " + mSteps.getTotalSteps() + ", current: " + mSteps.getStepsPerMinute(false));
|
||||
|
||||
// refreshCurrentSteps();
|
||||
// addEntries();
|
||||
}
|
||||
|
||||
private void refreshCurrentSteps() {
|
||||
private void addEntries() {
|
||||
mTotalStepsChart.setSingleEntryYValue(mSteps.getTotalSteps());
|
||||
YAxis stepsPerMinuteCurrentYAxis = mStepsPerMinuteCurrentChart.getAxisLeft();
|
||||
int maxStepsPerMinute = mSteps.getMaxStepsPerMinute();
|
||||
|
@ -180,24 +206,36 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
int stepsPerMinute = mSteps.getStepsPerMinute(true);
|
||||
mStepsPerMinuteCurrentChart.setSingleEntryYValue(stepsPerMinute);
|
||||
|
||||
if (mStepsPerMinuteHistoryChart.getData() == null) {
|
||||
if (mSteps.getTotalSteps() == 0) {
|
||||
return; // ignore the first default value to keep the "no-data-description" visible
|
||||
}
|
||||
LineData data = new LineData();
|
||||
mStepsPerMinuteHistoryChart.setData(data);
|
||||
data.addDataSet(mHistorySet);
|
||||
if (!addHistoryDataSet(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData();
|
||||
historyData.addXValue("");
|
||||
historyData.addEntry(new Entry(stepsPerMinute, mHistorySet.getEntryCount()), 0);
|
||||
ChartData data = mStepsPerMinuteHistoryChart.getData();
|
||||
data.addXValue("");
|
||||
if (stepsPerMinute < 0) {
|
||||
stepsPerMinute = 0;
|
||||
}
|
||||
mHistorySet.addEntry(new Entry(stepsPerMinute, data.getXValCount() - 1));
|
||||
int hr = getCurrentHeartRate();
|
||||
if (hr < 0) {
|
||||
hr = 0;
|
||||
}
|
||||
mHeartRateSet.addEntry(new Entry(hr, data.getXValCount() - 1));
|
||||
}
|
||||
|
||||
mTotalStepsData.notifyDataSetChanged();
|
||||
mStepsPerMinuteData.notifyDataSetChanged();
|
||||
mStepsPerMinuteHistoryChart.notifyDataSetChanged();
|
||||
|
||||
renderCharts();
|
||||
private boolean addHistoryDataSet(boolean force) {
|
||||
if (mStepsPerMinuteHistoryChart.getData() == null) {
|
||||
// ignore the first default value to keep the "no-data-description" visible
|
||||
if (force || mSteps.getTotalSteps() > 0) {
|
||||
LineData data = new LineData();
|
||||
data.addDataSet(mHistorySet);
|
||||
data.addDataSet(mHeartRateSet);
|
||||
mStepsPerMinuteHistoryChart.setData(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -205,6 +243,8 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
IntentFilter filterLocal = new IntentFilter();
|
||||
filterLocal.addAction(DeviceService.ACTION_REALTIME_STEPS);
|
||||
filterLocal.addAction(DeviceService.ACTION_HEARTRATE_MEASUREMENT);
|
||||
heartRateValues = new ArrayList<>();
|
||||
|
||||
View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false);
|
||||
|
||||
|
@ -227,16 +267,12 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (pulseScheduler != null) {
|
||||
pulseScheduler.shutdownNow();
|
||||
pulseScheduler = null;
|
||||
}
|
||||
stopActivityPulse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
pulseScheduler = startActivityPulse();
|
||||
}
|
||||
|
||||
private ScheduledExecutorService startActivityPulse() {
|
||||
|
@ -258,11 +294,33 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
return service;
|
||||
}
|
||||
|
||||
private void stopActivityPulse() {
|
||||
if (pulseScheduler != null) {
|
||||
pulseScheduler.shutdownNow();
|
||||
pulseScheduler = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in the UI thread.
|
||||
*/
|
||||
private void pulse() {
|
||||
refreshCurrentSteps();
|
||||
addEntries();
|
||||
|
||||
LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData();
|
||||
if (historyData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
historyData.notifyDataChanged();
|
||||
mTotalStepsData.notifyDataSetChanged();
|
||||
mStepsPerMinuteData.notifyDataSetChanged();
|
||||
mStepsPerMinuteHistoryChart.notifyDataSetChanged();
|
||||
|
||||
renderCharts();
|
||||
|
||||
// have to enable it again and again to keep it measureing
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true);
|
||||
}
|
||||
|
||||
private long getPulseIntervalMillis() {
|
||||
|
@ -272,15 +330,19 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
@Override
|
||||
protected void onMadeVisibleInActivity() {
|
||||
GBApplication.deviceService().onEnableRealtimeSteps(true);
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true);
|
||||
super.onMadeVisibleInActivity();
|
||||
if (getActivity() != null) {
|
||||
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
pulseScheduler = startActivityPulse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMadeInvisibleInActivity() {
|
||||
stopActivityPulse();
|
||||
GBApplication.deviceService().onEnableRealtimeSteps(false);
|
||||
GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(false);
|
||||
if (getActivity() != null) {
|
||||
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
@ -346,6 +408,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
private void setupHistoryChart(BarLineChartBase chart) {
|
||||
configureBarLineChartDefaults(chart);
|
||||
|
||||
chart.setTouchEnabled(false); // no zooming or anything, because it's updated all the time
|
||||
chart.setBackgroundColor(BACKGROUND_COLOR);
|
||||
chart.setDescriptionColor(DESCRIPTION_COLOR);
|
||||
chart.setDescription(getString(R.string.live_activity_steps_per_minute_history));
|
||||
|
@ -367,22 +430,28 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
y.setDrawGridLines(false);
|
||||
y.setDrawTopYLabelEntry(false);
|
||||
y.setTextColor(CHART_TEXT_COLOR);
|
||||
|
||||
y.setEnabled(true);
|
||||
y.setAxisMinValue(0);
|
||||
|
||||
YAxis yAxisRight = chart.getAxisRight();
|
||||
yAxisRight.setDrawGridLines(false);
|
||||
yAxisRight.setEnabled(false);
|
||||
yAxisRight.setDrawLabels(false);
|
||||
yAxisRight.setEnabled(true);
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(false);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
|
||||
mHistorySet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_steps_history));
|
||||
mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT);
|
||||
mHistorySet.setColor(akActivity.color);
|
||||
mHistorySet.setDrawCircles(false);
|
||||
mHistorySet.setDrawCubic(true);
|
||||
mHistorySet.setDrawFilled(true);
|
||||
mHistorySet.setDrawValues(false);
|
||||
|
||||
mHeartRateSet = createHeartrateSet(new ArrayList<Entry>(), getString(R.string.live_activity_heart_rate));
|
||||
mHeartRateSet.setDrawValues(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -402,7 +471,13 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db, GBDevice device) {
|
||||
protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChartsnUIThread(ChartsData chartsData) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.List;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount;
|
||||
|
@ -48,14 +49,16 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
private int mSmartAlarmGoneOff = -1;
|
||||
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db, GBDevice device) {
|
||||
protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||
List<ActivitySample> samples = getSamples(db, device);
|
||||
|
||||
refresh(device, mActivityChart, samples);
|
||||
refreshSleepAmounts(device, mSleepAmountChart, samples);
|
||||
MySleepChartsData mySleepChartsData = refreshSleepAmounts(device, samples);
|
||||
DefaultChartsData chartsData = refresh(device, samples);
|
||||
|
||||
return new MyChartsData(mySleepChartsData, chartsData);
|
||||
}
|
||||
|
||||
private void refreshSleepAmounts(GBDevice mGBDevice, PieChart pieChart, List<ActivitySample> samples) {
|
||||
private MySleepChartsData refreshSleepAmounts(GBDevice mGBDevice, List<ActivitySample> samples) {
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
ActivityAmounts amounts = analysis.calculateActivityAmounts(samples);
|
||||
PieData data = new PieData();
|
||||
|
@ -73,7 +76,6 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
}
|
||||
}
|
||||
String totalSleep = DateTimeUtils.formatDurationHoursMinutes(totalSeconds, TimeUnit.SECONDS);
|
||||
pieChart.setCenterText(totalSleep);
|
||||
PieDataSet set = new PieDataSet(entries, "");
|
||||
set.setValueFormatter(new ValueFormatter() {
|
||||
@Override
|
||||
|
@ -83,10 +85,18 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
});
|
||||
set.setColors(colors);
|
||||
data.setDataSet(set);
|
||||
pieChart.setData(data);
|
||||
|
||||
pieChart.getLegend().setEnabled(false);
|
||||
//setupLegend(pieChart);
|
||||
return new MySleepChartsData(totalSleep, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChartsnUIThread(ChartsData chartsData) {
|
||||
MyChartsData mcd = (MyChartsData) chartsData;
|
||||
mSleepAmountChart.setCenterText(mcd.getPieData().getTotalSleep());
|
||||
mSleepAmountChart.setData(mcd.getPieData().getPieData());
|
||||
|
||||
mActivityChart.setData(mcd.getChartsData().getCombinedData());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -132,6 +142,7 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
mSleepAmountChart.setDescription("");
|
||||
mSleepAmountChart.setNoDataTextDescription("");
|
||||
mSleepAmountChart.setNoDataText("");
|
||||
mSleepAmountChart.getLegend().setEnabled(false);
|
||||
}
|
||||
|
||||
private void setupActivityChart() {
|
||||
|
@ -164,8 +175,8 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(250);
|
||||
yAxisRight.setAxisMinValue(0);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
}
|
||||
|
||||
protected void setupLegend(Chart chart) {
|
||||
|
@ -194,4 +205,40 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||
mActivityChart.animateX(ANIM_TIME, Easing.EasingOption.EaseInOutQuart);
|
||||
mSleepAmountChart.invalidate();
|
||||
}
|
||||
|
||||
private static class MySleepChartsData extends ChartsData {
|
||||
private String totalSleep;
|
||||
private final PieData pieData;
|
||||
|
||||
public MySleepChartsData(String totalSleep, PieData pieData) {
|
||||
this.totalSleep = totalSleep;
|
||||
this.pieData = pieData;
|
||||
}
|
||||
|
||||
public PieData getPieData() {
|
||||
return pieData;
|
||||
}
|
||||
|
||||
public CharSequence getTotalSleep() {
|
||||
return totalSleep;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyChartsData extends ChartsData {
|
||||
private final DefaultChartsData chartsData;
|
||||
private final MySleepChartsData pieData;
|
||||
|
||||
public MyChartsData(MySleepChartsData pieData, DefaultChartsData chartsData) {
|
||||
this.pieData = pieData;
|
||||
this.chartsData = chartsData;
|
||||
}
|
||||
|
||||
public MySleepChartsData getPieData() {
|
||||
return pieData;
|
||||
}
|
||||
|
||||
public DefaultChartsData getChartsData() {
|
||||
return chartsData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,19 +42,30 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
private Locale mLocale;
|
||||
private int mTargetSteps = 10000;
|
||||
|
||||
private CombinedChart mWeekStepsChart;
|
||||
private PieChart mTodayStepsChart;
|
||||
private CombinedChart mWeekStepsChart;
|
||||
|
||||
@Override
|
||||
protected void refreshInBackground(DBHandler db, GBDevice device) {
|
||||
ChartsHost chartsHost = getChartsHost();
|
||||
if (chartsHost != null) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTime(chartsHost.getEndDate());
|
||||
//NB: we could have omitted the day, but this way we can move things to the past easily
|
||||
refreshDaySteps(db, mTodayStepsChart, day, device);
|
||||
refreshWeekBeforeSteps(db, mWeekStepsChart, day, device);
|
||||
}
|
||||
protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTime(chartsHost.getEndDate());
|
||||
//NB: we could have omitted the day, but this way we can move things to the past easily
|
||||
DaySteps daySteps = refreshDaySteps(db, day, device);
|
||||
DefaultChartsData weekBeforeStepsData = refreshWeekBeforeSteps(db, mWeekStepsChart, day, device);
|
||||
|
||||
return new MyChartsData(daySteps, weekBeforeStepsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChartsnUIThread(ChartsData chartsData) {
|
||||
MyChartsData mcd = (MyChartsData) chartsData;
|
||||
|
||||
// setupLegend(mWeekStepsChart);
|
||||
mTodayStepsChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(mcd.getDaySteps().totalSteps));
|
||||
mTodayStepsChart.setData(mcd.getDaySteps().data);
|
||||
|
||||
mWeekStepsChart.setData(mcd.getWeekBeforeStepsData().getCombinedData());
|
||||
mWeekStepsChart.getLegend().setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +74,7 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
mTodayStepsChart.invalidate();
|
||||
}
|
||||
|
||||
private void refreshWeekBeforeSteps(DBHandler db, CombinedChart combinedChart, Calendar day, GBDevice device) {
|
||||
private DefaultChartsData refreshWeekBeforeSteps(DBHandler db, CombinedChart combinedChart, Calendar day, GBDevice device) {
|
||||
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
|
||||
|
@ -90,18 +101,16 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
|
||||
CombinedData combinedData = new CombinedData(labels);
|
||||
combinedData.setData(barData);
|
||||
|
||||
setupLegend(combinedChart);
|
||||
combinedChart.setData(combinedData);
|
||||
combinedChart.getLegend().setEnabled(false);
|
||||
return new DefaultChartsData(combinedData);
|
||||
}
|
||||
|
||||
private void refreshDaySteps(DBHandler db, PieChart pieChart, Calendar day, GBDevice device) {
|
||||
|
||||
|
||||
private DaySteps refreshDaySteps(DBHandler db, Calendar day, GBDevice device) {
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
|
||||
int totalSteps = analysis.calculateTotalSteps(getSamplesOfDay(db, day, device));
|
||||
|
||||
pieChart.setCenterText(NumberFormat.getNumberInstance(mLocale).format(totalSteps));
|
||||
PieData data = new PieData();
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
List<Integer> colors = new ArrayList<>();
|
||||
|
@ -123,9 +132,8 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
data.setDataSet(set);
|
||||
//this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above
|
||||
data.setDrawValues(false);
|
||||
pieChart.setData(data);
|
||||
|
||||
pieChart.getLegend().setEnabled(false);
|
||||
return new DaySteps(data, totalSteps);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -164,6 +172,8 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
mTodayStepsChart.setDescription(getContext().getString(R.string.weeksteps_today_steps_description, mTargetSteps));
|
||||
mTodayStepsChart.setNoDataTextDescription("");
|
||||
mTodayStepsChart.setNoDataText("");
|
||||
mTodayStepsChart.getLegend().setEnabled(false);
|
||||
// setupLegend(mTodayStepsChart);
|
||||
}
|
||||
|
||||
private void setupWeekStepsChart() {
|
||||
|
@ -196,12 +206,12 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
}
|
||||
|
||||
protected void setupLegend(Chart chart) {
|
||||
List<Integer> legendColors = new ArrayList<>(1);
|
||||
List<String> legendLabels = new ArrayList<>(1);
|
||||
legendColors.add(akActivity.color);
|
||||
legendLabels.add(getContext().getString(R.string.chart_steps));
|
||||
chart.getLegend().setCustom(legendColors, legendLabels);
|
||||
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
// List<Integer> legendColors = new ArrayList<>(1);
|
||||
// List<String> legendLabels = new ArrayList<>(1);
|
||||
// legendColors.add(akActivity.color);
|
||||
// legendLabels.add(getContext().getString(R.string.chart_steps));
|
||||
// chart.getLegend().setCustom(legendColors, legendLabels);
|
||||
// chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
}
|
||||
|
||||
private List<ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, GBDevice device) {
|
||||
|
@ -226,4 +236,32 @@ public class WeekStepsChartFragment extends AbstractChartFragment {
|
|||
protected List<ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
return super.getAllSamples(db, device, tsFrom, tsTo);
|
||||
}
|
||||
|
||||
private static class DaySteps {
|
||||
private final PieData data;
|
||||
private final int totalSteps;
|
||||
|
||||
public DaySteps(PieData data, int totalSteps) {
|
||||
this.data = data;
|
||||
this.totalSteps = totalSteps;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyChartsData extends ChartsData {
|
||||
private final DefaultChartsData weekBeforeStepsData;
|
||||
private final DaySteps daySteps;
|
||||
|
||||
public MyChartsData(DaySteps daySteps, DefaultChartsData weekBeforeStepsData) {
|
||||
this.daySteps = daySteps;
|
||||
this.weekBeforeStepsData = weekBeforeStepsData;
|
||||
}
|
||||
|
||||
public DaySteps getDaySteps() {
|
||||
return daySteps;
|
||||
}
|
||||
|
||||
public DefaultChartsData getWeekBeforeStepsData() {
|
||||
return weekBeforeStepsData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ public interface EventHandler {
|
|||
|
||||
void onHeartRateTest();
|
||||
|
||||
void onEnableRealtimeHeartRateMeasurement(boolean enable);
|
||||
|
||||
void onFindDevice(boolean start);
|
||||
|
||||
void onScreenshotReq();
|
||||
|
|
|
@ -59,20 +59,14 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
|
|||
protected String[] getPreferenceKeysWithSummary() {
|
||||
return new String[]{
|
||||
PREF_USER_ALIAS,
|
||||
PREF_MIBAND_WEARSIDE,
|
||||
PREF_MIBAND_ADDRESS,
|
||||
PREF_MIBAND_FITNESS_GOAL,
|
||||
PREF_MIBAND_DONT_ACK_TRANSFER,
|
||||
PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR,
|
||||
getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_SMS),
|
||||
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS),
|
||||
getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_INCOMING_CALL),
|
||||
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL),
|
||||
getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_K9MAIL),
|
||||
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_K9MAIL),
|
||||
getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_PEBBLEMSG),
|
||||
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_PEBBLEMSG),
|
||||
getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_GENERIC),
|
||||
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_GENERIC),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -216,4 +216,11 @@ public class GBDeviceService implements DeviceService {
|
|||
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||
invokeService(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@ public interface DeviceService extends EventHandler {
|
|||
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
|
||||
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
||||
String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps";
|
||||
String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement";
|
||||
String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support";
|
||||
String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement";
|
||||
String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
||||
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
||||
|
@ -62,6 +64,7 @@ public interface DeviceService extends EventHandler {
|
|||
String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps";
|
||||
String EXTRA_REALTIME_STEPS = "realtime_steps";
|
||||
String EXTRA_TIMESTAMP = "timestamp";
|
||||
String EXTRA_HEART_RATE_VALUE = "hr_value";
|
||||
|
||||
void start();
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
public class Measurement {
|
||||
private final int value;
|
||||
private final long timestamp;
|
||||
|
||||
public Measurement(int value, long timestamp) {
|
||||
this.value = value;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (71 ^ value ^ timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Measurement) {
|
||||
Measurement m = (Measurement) o;
|
||||
return timestamp == m.timestamp && value == m.value;
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CO
|
|||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE;
|
||||
|
@ -349,6 +350,11 @@ public class DeviceCommunicationService extends Service {
|
|||
mDeviceSupport.onEnableHeartRateSleepSupport(enable);
|
||||
break;
|
||||
}
|
||||
case ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT: {
|
||||
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
|
||||
mDeviceSupport.onEnableRealtimeHeartRateMeasurement(enable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
|
|
|
@ -250,4 +250,12 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
|||
}
|
||||
delegate.onEnableHeartRateSleepSupport(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
if (checkBusy("enable realtime heart rate measurement: " + enable)) {
|
||||
return;
|
||||
}
|
||||
delegate.onEnableRealtimeHeartRateMeasurement(enable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -603,7 +603,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||
TransactionBuilder builder = performInitialized("HeartRateTest");
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
|
@ -614,6 +613,24 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
if (supportsHeartRate()) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement");
|
||||
if (enable) {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
}
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsHeartRate() {
|
||||
return getDeviceInfo() != null && getDeviceInfo().supportsHeartrate();
|
||||
}
|
||||
|
@ -745,7 +762,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||
} else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
handleRealtimeSteps(characteristic.getValue());
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
logHeartrate(characteristic.getValue());
|
||||
handleHeartrate(characteristic.getValue());
|
||||
} else {
|
||||
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
||||
logMessageContent(characteristic.getValue());
|
||||
|
@ -814,6 +831,15 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleHeartrate(byte[] value) {
|
||||
if (value.length == 2 && value[0] == 6) {
|
||||
int hrValue = (value[1] & 0xff);
|
||||
Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT)
|
||||
.putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue)
|
||||
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRealtimeSteps(byte[] value) {
|
||||
int steps = 0xff & value[0] | (0xff & value[1]) << 8;
|
||||
|
|
|
@ -181,4 +181,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
|||
byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable);
|
||||
sendToDevice(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeHeartRateMeasurement(enable);
|
||||
sendToDevice(bytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ public abstract class GBDeviceProtocol {
|
|||
return null;
|
||||
}
|
||||
|
||||
public byte[] encodeEnableRealtimeHeartRateMeasurement(boolean enable) { return null; }
|
||||
|
||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||
return null;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 971 B After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1008 B After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 961 B |
Before Width: | Height: | Size: 650 B After Width: | Height: | Size: 1021 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -1,32 +0,0 @@
|
|||
<resources>
|
||||
|
||||
<style name="GadgetbridgeTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:colorPrimary">@color/primary_light</item>
|
||||
<item name="android:colorPrimaryDark">@color/primarydark_light</item>
|
||||
<item name="android:colorAccent">@color/accent</item>
|
||||
<item name="android:textColor">@color/primary_text_default_material_light</item>
|
||||
<item name="colorPrimary">@color/primary_light</item>
|
||||
<item name="colorPrimaryDark">@color/primarydark_light</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
|
||||
<!-- unfortunately it doesn't work this way :( -->
|
||||
<!--
|
||||
<item name="chart_deep_sleep">@color/chart_deep_sleep_light</item>
|
||||
<item name="chart_light_sleep">@color/chart_light_sleep_light</item>
|
||||
<item name="chart_activity">@color/chart_activity_light</item>
|
||||
-->
|
||||
</style>
|
||||
<style name="GadgetbridgeThemeDark" parent="@android:style/Theme.Material">
|
||||
<item name="android:colorPrimary">@color/primary_dark</item>
|
||||
<item name="android:colorPrimaryDark">@color/primarydark_dark</item>
|
||||
<item name="android:colorAccent">@color/accent</item>
|
||||
<item name="android:textColor">@color/primary_text_default_material_dark</item>
|
||||
|
||||
<!-- unfortunately it doesn't work this way :( -->
|
||||
<!--
|
||||
<item name="chart_deep_sleep">@color/chart_deep_sleep_dark</item>
|
||||
<item name="chart_light_sleep">@color/chart_light_sleep_dark</item>
|
||||
<item name="chart_activity">@color/chart_activity_dark</item>
|
||||
-->
|
||||
</style>
|
||||
</resources>
|
|
@ -1,5 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_theme_options">
|
||||
<item>@string/pref_theme_light</item>
|
||||
<item>@string/pref_theme_dark</item>
|
||||
</string-array>
|
||||
<string-array name="pref_theme_values">
|
||||
<item>@string/pref_theme_value_light</item>
|
||||
<item>@string/pref_theme_value_dark</item>
|
||||
</string-array>
|
||||
<string name="pref_theme_value_light" translatable="false">light</string>
|
||||
<string name="pref_theme_value_dark" translatable="false">dark</string>
|
||||
|
||||
<string-array name="notification_mode">
|
||||
<item>@string/always</item>
|
||||
<item>@string/when_screen_off</item>
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
<color name="primarydark_dark" type="color">#f0f03000</color>
|
||||
|
||||
<color name="accent" type="color">#0091ea</color>
|
||||
<color name="primarytext" type="color">#ff000000</color>
|
||||
<color name="primarytext_light" type="color">#000000</color>
|
||||
<color name="primarytext_dark" type="color">#ffffff</color>
|
||||
<color name="secondarytext" type="color">#ff808080</color>
|
||||
<color name="divider" type="color">#1f000000</color>
|
||||
|
||||
<color name="chart_heartrate" type="color">#ffab40</color>
|
||||
<color name="chart_heartrate_fill" type="color">#fadab1</color>
|
||||
<color name="chart_deep_sleep_light" type="color">#0071b7</color>
|
||||
<color name="chart_deep_sleep_dark" type="color">#4c5aff</color>
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
<string name="pref_header_datetime">Date and Time</string>
|
||||
<string name="pref_title_datetime_syctimeonconnect">Sync time</string>
|
||||
<string name="pref_summary_datetime_syctimeonconnect">Sync time to device when connecting and when time or timezone changes on Android</string>
|
||||
<string name="pref_title_theme">Theme</string>
|
||||
<string name="pref_theme_light">Light</string>
|
||||
<string name="pref_theme_dark">Dark</string>
|
||||
|
||||
<string name="pref_header_notifications">Notifications</string>
|
||||
<string name="pref_title_notifications_repetitions">Repetitions</string>
|
||||
|
@ -240,5 +243,6 @@
|
|||
<string name="updatefirmwareoperation_update_in_progress">Firmware update in progress</string>
|
||||
<string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string>
|
||||
<string name="charts_legend_heartrate">Heart Rate</string>
|
||||
<string name="live_activity_heart_rate">Heart Rate</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
<resources>
|
||||
|
||||
<style name="GadgetbridgeTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:actionBarStyle">@style/GadgetbridgeActionBar</item>
|
||||
<item name="android:actionBarWidgetTheme">@style/GadgetbridgeActionBarWidget</item>
|
||||
<item name="android:textColor">@color/primarytext</item>
|
||||
<item name="android:textColor">@color/primarytext_light</item>
|
||||
<item name="colorPrimary">@color/primary_light</item>
|
||||
<item name="colorPrimaryDark">@color/primarydark_light</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
|
||||
<!-- unfortunately it doesn't work this way :( -->
|
||||
<!--
|
||||
<item name="chart_deep_sleep">@color/chart_deep_sleep_light</item>
|
||||
<item name="chart_light_sleep">@color/chart_light_sleep_light</item>
|
||||
<item name="chart_activity">@color/chart_activity_light</item>
|
||||
-->
|
||||
</style>
|
||||
<style name="GadgetbridgeThemeDark" parent="Theme.AppCompat">
|
||||
<item name="android:textColor">@color/primarytext_dark</item>
|
||||
<item name="colorPrimary">@color/primary_dark</item>
|
||||
<item name="colorPrimaryDark">@color/primarydark_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
|
||||
<style name="GadgetbridgeActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
|
||||
<item name="android:background">@color/primary_light</item>
|
||||
<item name="android:backgroundStacked">@color/primarydark_light</item>
|
||||
<item name="android:displayOptions">showHome|showTitle</item>
|
||||
<item name="android:icon">@android:color/transparent</item>
|
||||
<!-- unfortunately it doesn't work this way :( -->
|
||||
<!--
|
||||
<item name="chart_deep_sleep">@color/chart_deep_sleep_dark</item>
|
||||
<item name="chart_light_sleep">@color/chart_light_sleep_dark</item>
|
||||
<item name="chart_activity">@color/chart_activity_dark</item>
|
||||
-->
|
||||
</style>
|
||||
|
||||
<style name="GadgetbridgeActionBarWidget" parent="android:Theme.Holo.Light">
|
||||
<item name="android:popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
|
||||
<item name="android:dropDownListViewStyle">@android:style/Widget.Holo.Light.ListView</item>
|
||||
</style>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
android:entries="@array/wearside"
|
||||
android:entryValues="@array/wearside_values"
|
||||
android:key="mi_wearside"
|
||||
android:title="@string/miband_prefs_wearside" />
|
||||
android:title="@string/miband_prefs_wearside"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="10000"
|
||||
|
@ -50,7 +51,8 @@
|
|||
android:entries="@array/vibration_profile"
|
||||
android:entryValues="@array/vibration_profile_values"
|
||||
android:key="mi_vibration_profile_sms"
|
||||
android:title="@string/miband_prefs_vibration" />
|
||||
android:title="@string/miband_prefs_vibration"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="3"
|
||||
|
@ -70,7 +72,8 @@
|
|||
android:entries="@array/vibration_profile"
|
||||
android:entryValues="@array/vibration_profile_values"
|
||||
android:key="mi_vibration_profile_incoming_call"
|
||||
android:title="@string/miband_prefs_vibration" />
|
||||
android:title="@string/miband_prefs_vibration"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="60"
|
||||
|
@ -90,7 +93,8 @@
|
|||
android:entries="@array/vibration_profile"
|
||||
android:entryValues="@array/vibration_profile_values"
|
||||
android:key="mi_vibration_profile_k9mail"
|
||||
android:title="@string/miband_prefs_vibration" />
|
||||
android:title="@string/miband_prefs_vibration"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="2"
|
||||
|
@ -110,7 +114,8 @@
|
|||
android:entries="@array/vibration_profile"
|
||||
android:entryValues="@array/vibration_profile_values"
|
||||
android:key="mi_vibration_profile_pebblemsg"
|
||||
android:title="@string/miband_prefs_vibration" />
|
||||
android:title="@string/miband_prefs_vibration"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="1"
|
||||
|
@ -130,7 +135,8 @@
|
|||
android:entries="@array/vibration_profile"
|
||||
android:entryValues="@array/vibration_profile_values"
|
||||
android:key="mi_vibration_profile_generic"
|
||||
android:title="@string/miband_prefs_vibration" />
|
||||
android:title="@string/miband_prefs_vibration"
|
||||
android:summary="%s" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="3"
|
||||
|
|
|
@ -10,7 +10,15 @@
|
|||
<ListPreference
|
||||
android:defaultValue="default"
|
||||
android:key="audio_player"
|
||||
android:title="@string/pref_title_audo_player" />
|
||||
android:title="@string/pref_title_audo_player"
|
||||
android:summary="%s" />
|
||||
<ListPreference
|
||||
android:key="pref_key_theme"
|
||||
android:title="@string/pref_title_theme"
|
||||
android:entries="@array/pref_theme_options"
|
||||
android:entryValues="@array/pref_theme_values"
|
||||
android:defaultValue="@string/pref_theme_value_light"
|
||||
android:summary="%s" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_datetime"
|
||||
|
@ -30,21 +38,24 @@
|
|||
android:entries="@array/notification_mode_toggle"
|
||||
android:entryValues="@array/notification_mode_values_toggle"
|
||||
android:key="notification_mode_calls"
|
||||
android:title="@string/pref_title_notifications_call" />
|
||||
android:title="@string/pref_title_notifications_call"
|
||||
android:summary="%s" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="when_screen_off"
|
||||
android:entries="@array/notification_mode"
|
||||
android:entryValues="@array/notification_mode_values"
|
||||
android:key="notification_mode_sms"
|
||||
android:title="@string/pref_title_notifications_sms" />
|
||||
android:title="@string/pref_title_notifications_sms"
|
||||
android:summary="%s" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="when_screen_off"
|
||||
android:entries="@array/notification_mode"
|
||||
android:entryValues="@array/notification_mode_values"
|
||||
android:key="notification_mode_k9mail"
|
||||
android:title="@string/pref_title_notifications_k9mail" />
|
||||
android:title="@string/pref_title_notifications_k9mail"
|
||||
android:summary="%s" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="when_screen_off"
|
||||
|
@ -139,7 +150,8 @@
|
|||
android:entries="@array/gender"
|
||||
android:entryValues="@array/gender_values"
|
||||
android:key="activity_user_gender"
|
||||
android:title="@string/activity_prefs_gender" />
|
||||
android:title="@string/activity_prefs_gender"
|
||||
android:summary="%s" />
|
||||
|
||||
<!--TODO: support localized heights and weights -->
|
||||
<EditTextPreference
|
||||
|
@ -191,7 +203,8 @@
|
|||
android:entries="@array/pebble_activitytracker"
|
||||
android:entryValues="@array/pebble_activitytracker_values"
|
||||
android:key="pebble_activitytracker"
|
||||
android:title="@string/pref_title_pebble_activitytracker" />
|
||||
android:title="@string/pref_title_pebble_activitytracker"
|
||||
android:summary="%s" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_development"
|
||||
|
|
|
@ -129,4 +129,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport {
|
|||
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip
|
||||
|
|