mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-12 13:09:24 +01:00
Merge branch 'master' into db-refactoring
This commit is contained in:
commit
22a5aef7d7
@ -1,7 +1,12 @@
|
|||||||
language: android
|
language: android
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
- oraclejdk7
|
- oraclejdk7
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GRADLE_OPTS="-XX:MaxPermSize=256m"
|
||||||
|
|
||||||
android:
|
android:
|
||||||
components:
|
components:
|
||||||
# Uncomment the lines below if you want to
|
# Uncomment the lines below if you want to
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
|||||||
###Changelog
|
###Changelog
|
||||||
|
|
||||||
|
####Version (next)
|
||||||
|
* Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!)
|
||||||
|
* Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly
|
||||||
|
* Pebble: delay between reconnection attempts (from 1 up to 64 seconds)
|
||||||
|
|
||||||
|
####Version 0.9.3
|
||||||
|
* Pebble: Fix Pebble Health activation (was not available in the App Manager)
|
||||||
|
* Simplify connection state display (only connecting->connected)
|
||||||
|
* Small improvements to the pairing activity
|
||||||
|
* Mi Band 1S: Fix for mi band firmware update
|
||||||
|
|
||||||
####Version 0.9.2
|
####Version 0.9.2
|
||||||
* Mi Band: Fix update of second (HR) firmware on Mi1S (#234)
|
* Mi Band: Fix update of second (HR) firmware on Mi1S (#234)
|
||||||
* Fix ordering issue of device infos being displayed
|
* Fix ordering issue of device infos being displayed
|
||||||
|
@ -16,8 +16,8 @@ android {
|
|||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
|
|
||||||
// note: always bump BOTH versionCode and versionName!
|
// note: always bump BOTH versionCode and versionName!
|
||||||
versionName "0.9.2"
|
versionName "0.9.4"
|
||||||
versionCode 46
|
versionCode 48
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
149
app/src/main/assets/ic_launcher.svg
Normal file
149
app/src/main/assets/ic_launcher.svg
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
viewBox="0 0 1814.1732 1814.1732"
|
||||||
|
height="512mm"
|
||||||
|
width="512mm"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="211ad552-a817-11e5-98b9-385bef8cd413.svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1600"
|
||||||
|
inkscape:window-height="876"
|
||||||
|
id="namedview4212"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.4040408"
|
||||||
|
inkscape:cx="498.78758"
|
||||||
|
inkscape:cy="883.13571"
|
||||||
|
inkscape:window-x="1024"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<marker
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="marker4512"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
id="path4514"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#1976d2;stroke-width:1pt;stroke-opacity:1;fill:#ffffff;fill-opacity:1"
|
||||||
|
transform="scale(0.8) translate(12.5,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="Arrow1Lstart"
|
||||||
|
refX="0.0"
|
||||||
|
refY="0.0"
|
||||||
|
orient="auto">
|
||||||
|
<path
|
||||||
|
transform="scale(0.8) translate(12.5,0)"
|
||||||
|
style="fill-rule:evenodd;stroke:#1976d2;stroke-width:1pt;stroke-opacity:1;fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
id="path4208" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<circle
|
||||||
|
style="fill:#1976d2;fill-opacity:1;stroke:none;stroke-width:5.9000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path4178"
|
||||||
|
cx="899.03583"
|
||||||
|
cy="935.34052"
|
||||||
|
r="652.55853" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000;fill-opacity:0.24025975;stroke:none;stroke-width:5.9000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1180.6138,761.25155 -397.79692,586.48635 230.40242,230.4024 a 652.55853,652.55853 0 0 0 515.1523,-469.1328 L 1180.6138,761.25155 Z"
|
||||||
|
id="path4178-3" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#1976d2;stroke-width:13.60000038;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 703.29568,640.10033 c 0,0 -62.09786,90.61312 -84.48779,144.65142 -26.49785,122.73311 44.15742,168.66683 151.63581,319.05845 l -2.08379,245.8099 251.87809,0 0,-245.8099 c 0,0 77.6015,-28.5779 110.2687,-109.23097 30.2773,-74.75286 27.0683,-193.76648 27.0683,-193.76648 z"
|
||||||
|
id="path4176" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-width:13.60000038;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4136"
|
||||||
|
width="109.0837"
|
||||||
|
height="219.8989"
|
||||||
|
x="1289.9961"
|
||||||
|
y="43.667068"
|
||||||
|
ry="43.04707"
|
||||||
|
transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-width:13.60000038;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4136-5"
|
||||||
|
width="109.0837"
|
||||||
|
height="252.79712"
|
||||||
|
x="1180.0483"
|
||||||
|
y="40.667068"
|
||||||
|
ry="49.487179"
|
||||||
|
transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-width:13.60000038;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4136-8"
|
||||||
|
width="109.0837"
|
||||||
|
height="297.81583"
|
||||||
|
x="1070.1006"
|
||||||
|
y="37.667068"
|
||||||
|
ry="58.299976"
|
||||||
|
transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-width:13.60000038;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4136-1"
|
||||||
|
width="109.0837"
|
||||||
|
height="219.8989"
|
||||||
|
x="958.4209"
|
||||||
|
y="35.667068"
|
||||||
|
ry="43.04707"
|
||||||
|
transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,0)" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#1976d2;stroke-width:12.19999981;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 707.67725,640.85267 143.17855,38.54881 c 0,0 39.4733,34.66779 27.90054,81.29964 -10.45038,42.10926 -58.66289,44.51639 -58.66289,44.51639 L 705.87743,773.82104"
|
||||||
|
id="path4186" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ff9800;fill-opacity:1;stroke:#ff9800;stroke-width:12.48825073;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
|
||||||
|
id="rect4628"
|
||||||
|
width="314.25906"
|
||||||
|
height="96.066086"
|
||||||
|
x="738.79462"
|
||||||
|
y="1163.3024"
|
||||||
|
ry="7.8193355" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1976d2;fill-opacity:1;stroke:#1976d2;stroke-width:8.70396328;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4624"
|
||||||
|
width="171.0025"
|
||||||
|
height="47.28043"
|
||||||
|
x="810.42285"
|
||||||
|
y="1187.136"
|
||||||
|
ry="15.011105" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
@ -33,7 +33,7 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateSummary(Preference preference, Object value) {
|
public void updateSummary(Preference preference, Object value) {
|
||||||
String stringValue = value.toString();
|
String stringValue = String.valueOf(value);
|
||||||
|
|
||||||
if (preference instanceof ListPreference) {
|
if (preference instanceof ListPreference) {
|
||||||
// For list preferences, look up the correct display value in
|
// For list preferences, look up the correct display value in
|
||||||
|
@ -80,7 +80,7 @@ public class AppManagerActivity extends Activity {
|
|||||||
List<GBDeviceApp> systemApps = new ArrayList<>();
|
List<GBDeviceApp> systemApps = new ArrayList<>();
|
||||||
systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
||||||
systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
||||||
if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getFirmwareVersion()))) {
|
if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) {
|
||||||
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,10 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
@ -118,20 +119,20 @@ public class DebugActivity extends Activity {
|
|||||||
incomingCallButton.setOnClickListener(new View.OnClickListener() {
|
incomingCallButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onSetCallState(
|
CallSpec callSpec = new CallSpec();
|
||||||
editContent.getText().toString(),
|
callSpec.command = CallSpec.CALL_INCOMING;
|
||||||
null,
|
callSpec.number = editContent.getText().toString();
|
||||||
ServiceCommand.CALL_INCOMING);
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
outgoingCallButton = (Button) findViewById(R.id.outgoingCallButton);
|
outgoingCallButton = (Button) findViewById(R.id.outgoingCallButton);
|
||||||
outgoingCallButton.setOnClickListener(new View.OnClickListener() {
|
outgoingCallButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onSetCallState(
|
CallSpec callSpec = new CallSpec();
|
||||||
editContent.getText().toString(),
|
callSpec.command = CallSpec.CALL_OUTGOING;
|
||||||
null,
|
callSpec.number = editContent.getText().toString();
|
||||||
ServiceCommand.CALL_OUTGOING);
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -139,20 +140,18 @@ public class DebugActivity extends Activity {
|
|||||||
startCallButton.setOnClickListener(new View.OnClickListener() {
|
startCallButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onSetCallState(
|
CallSpec callSpec = new CallSpec();
|
||||||
null,
|
callSpec.command = CallSpec.CALL_START;
|
||||||
null,
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
ServiceCommand.CALL_START);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
endCallButton = (Button) findViewById(R.id.endCallButton);
|
endCallButton = (Button) findViewById(R.id.endCallButton);
|
||||||
endCallButton.setOnClickListener(new View.OnClickListener() {
|
endCallButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onSetCallState(
|
CallSpec callSpec = new CallSpec();
|
||||||
null,
|
callSpec.command = CallSpec.CALL_END;
|
||||||
null,
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
ServiceCommand.CALL_END);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -199,10 +198,15 @@ public class DebugActivity extends Activity {
|
|||||||
setMusicInfoButton.setOnClickListener(new View.OnClickListener() {
|
setMusicInfoButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onSetMusicInfo(
|
MusicSpec musicSpec = new MusicSpec();
|
||||||
editContent.getText().toString() + "(artist)",
|
musicSpec.artist = editContent.getText().toString() + "(artist)";
|
||||||
editContent.getText().toString() + "(album)",
|
musicSpec.album = editContent.getText().toString() + "(album)";
|
||||||
editContent.getText().toString() + "(track)", 20, 10, 2);
|
musicSpec.track = editContent.getText().toString() + "(track)";
|
||||||
|
musicSpec.duration = 10;
|
||||||
|
musicSpec.trackCount = 5;
|
||||||
|
musicSpec.trackNr = 2;
|
||||||
|
|
||||||
|
GBApplication.deviceService().onSetMusicInfo(musicSpec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.adapter.ItemWithDetailsAdapter;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|||||||
public class FwAppInstallerActivity extends Activity implements InstallActivity {
|
public class FwAppInstallerActivity extends Activity implements InstallActivity {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class);
|
||||||
|
private static final String ITEM_DETAILS = "details";
|
||||||
|
|
||||||
private TextView fwAppInstallTextView;
|
private TextView fwAppInstallTextView;
|
||||||
private Button installButton;
|
private Button installButton;
|
||||||
@ -45,13 +47,22 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
private InstallHandler installHandler;
|
private InstallHandler installHandler;
|
||||||
private boolean mayConnect;
|
private boolean mayConnect;
|
||||||
|
|
||||||
|
private ProgressBar mProgressBar;
|
||||||
|
private ListView itemListView;
|
||||||
|
private final List<ItemWithDetails> mItems = new ArrayList<>();
|
||||||
|
private ItemWithDetailsAdapter mItemAdapter;
|
||||||
|
|
||||||
|
private ListView detailsListView;
|
||||||
|
private ItemWithDetailsAdapter mDetailsItemAdapter;
|
||||||
|
private ArrayList<ItemWithDetails> mDetails = new ArrayList<>();
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
if (action.equals(GBApplication.ACTION_QUIT)) {
|
if (GBApplication.ACTION_QUIT.equals(action)) {
|
||||||
finish();
|
finish();
|
||||||
} else if (action.equals(GBDevice.ACTION_DEVICE_CHANGED)) {
|
} else if (GBDevice.ACTION_DEVICE_CHANGED.equals(action)) {
|
||||||
device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
refreshBusyState(device);
|
refreshBusyState(device);
|
||||||
@ -67,13 +78,13 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
validateInstallation();
|
validateInstallation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (GB.ACTION_DISPLAY_MESSAGE.equals(action)) {
|
||||||
|
String message = intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE);
|
||||||
|
int severity = intent.getIntExtra(GB.DISPLAY_MESSAGE_SEVERITY, GB.INFO);
|
||||||
|
addMessage(message, severity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private ProgressBar mProgressBar;
|
|
||||||
private ListView itemListView;
|
|
||||||
private final List<ItemWithDetails> mItems = new ArrayList<>();
|
|
||||||
private ItemWithDetailsAdapter mItemAdapter;
|
|
||||||
|
|
||||||
private void refreshBusyState(GBDevice dev) {
|
private void refreshBusyState(GBDevice dev) {
|
||||||
if (dev.isConnecting() || dev.isBusy()) {
|
if (dev.isConnecting() || dev.isBusy()) {
|
||||||
@ -107,6 +118,13 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
if (dev != null) {
|
if (dev != null) {
|
||||||
device = dev;
|
device = dev;
|
||||||
}
|
}
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mDetails = savedInstanceState.getParcelableArrayList(ITEM_DETAILS);
|
||||||
|
if (mDetails == null) {
|
||||||
|
mDetails = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mayConnect = true;
|
mayConnect = true;
|
||||||
itemListView = (ListView) findViewById(R.id.itemListView);
|
itemListView = (ListView) findViewById(R.id.itemListView);
|
||||||
mItemAdapter = new ItemWithDetailsAdapter(this, mItems);
|
mItemAdapter = new ItemWithDetailsAdapter(this, mItems);
|
||||||
@ -114,10 +132,15 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
fwAppInstallTextView = (TextView) findViewById(R.id.infoTextView);
|
fwAppInstallTextView = (TextView) findViewById(R.id.infoTextView);
|
||||||
installButton = (Button) findViewById(R.id.installButton);
|
installButton = (Button) findViewById(R.id.installButton);
|
||||||
mProgressBar = (ProgressBar) findViewById(R.id.installProgressBar);
|
mProgressBar = (ProgressBar) findViewById(R.id.installProgressBar);
|
||||||
|
detailsListView = (ListView) findViewById(R.id.detailsListView);
|
||||||
|
mDetailsItemAdapter = new ItemWithDetailsAdapter(this, mDetails);
|
||||||
|
mDetailsItemAdapter.setSize(ItemWithDetailsAdapter.SIZE_SMALL);
|
||||||
|
detailsListView.setAdapter(mDetailsItemAdapter);
|
||||||
setInstallEnabled(false);
|
setInstallEnabled(false);
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(GBApplication.ACTION_QUIT);
|
filter.addAction(GBApplication.ACTION_QUIT);
|
||||||
filter.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
filter.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
||||||
|
filter.addAction(GB.ACTION_DISPLAY_MESSAGE);
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
||||||
|
|
||||||
installButton.setOnClickListener(new View.OnClickListener() {
|
installButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -145,6 +168,12 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putParcelableArrayList(ITEM_DETAILS, mDetails);
|
||||||
|
}
|
||||||
|
|
||||||
private InstallHandler findInstallHandlerFor(Uri uri) {
|
private InstallHandler findInstallHandlerFor(Uri uri) {
|
||||||
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
||||||
InstallHandler handler = coordinator.findInstallHandler(uri, this);
|
InstallHandler handler = coordinator.findInstallHandler(uri, this);
|
||||||
@ -195,4 +224,9 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity
|
|||||||
mItems.add(item);
|
mItems.add(item);
|
||||||
mItemAdapter.notifyDataSetChanged();
|
mItemAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMessage(String message, int severity) {
|
||||||
|
mDetails.add(new GenericItem(message));
|
||||||
|
mDetailsItemAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
@ -79,7 +80,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
private boolean mChartDirty = true;
|
private boolean mChartDirty = true;
|
||||||
private boolean supportsHeartrateChart = false;
|
private boolean supportsHeartrateChart = true;
|
||||||
|
private AsyncTask refreshTask;
|
||||||
|
|
||||||
public boolean isChartDirty() {
|
public boolean isChartDirty() {
|
||||||
return mChartDirty;
|
return mChartDirty;
|
||||||
@ -119,6 +121,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
protected int AK_LIGHT_SLEEP_COLOR;
|
protected int AK_LIGHT_SLEEP_COLOR;
|
||||||
protected int AK_NOT_WORN_COLOR;
|
protected int AK_NOT_WORN_COLOR;
|
||||||
|
|
||||||
|
protected String HEARTRATE_LABEL;
|
||||||
|
|
||||||
protected AbstractChartFragment(String... intentFilterActions) {
|
protected AbstractChartFragment(String... intentFilterActions) {
|
||||||
mIntentFilterActions = new HashSet<>();
|
mIntentFilterActions = new HashSet<>();
|
||||||
if (intentFilterActions != null) {
|
if (intentFilterActions != null) {
|
||||||
@ -153,6 +157,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light);
|
AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light);
|
||||||
AK_NOT_WORN_COLOR = getResources().getColor(R.color.chart_not_worn_light);
|
AK_NOT_WORN_COLOR = getResources().getColor(R.color.chart_not_worn_light);
|
||||||
|
|
||||||
|
HEARTRATE_LABEL = getContext().getString(R.string.charts_legend_heartrate);
|
||||||
|
|
||||||
akActivity = new ActivityConfig(ActivityKind.TYPE_ACTIVITY, getString(R.string.abstract_chart_fragment_kind_activity), AK_ACTIVITY_COLOR);
|
akActivity = new ActivityConfig(ActivityKind.TYPE_ACTIVITY, getString(R.string.abstract_chart_fragment_kind_activity), AK_ACTIVITY_COLOR);
|
||||||
akLightSleep = new ActivityConfig(ActivityKind.TYPE_LIGHT_SLEEP, getString(R.string.abstract_chart_fragment_kind_light_sleep), AK_LIGHT_SLEEP_COLOR);
|
akLightSleep = new ActivityConfig(ActivityKind.TYPE_LIGHT_SLEEP, getString(R.string.abstract_chart_fragment_kind_light_sleep), AK_LIGHT_SLEEP_COLOR);
|
||||||
akDeepSleep = new ActivityConfig(ActivityKind.TYPE_DEEP_SLEEP, getString(R.string.abstract_chart_fragment_kind_deep_sleep), AK_DEEP_SLEEP_COLOR);
|
akDeepSleep = new ActivityConfig(ActivityKind.TYPE_DEEP_SLEEP, getString(R.string.abstract_chart_fragment_kind_deep_sleep), AK_DEEP_SLEEP_COLOR);
|
||||||
@ -363,7 +369,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
if (chartsHost.getDevice() != null) {
|
if (chartsHost.getDevice() != null) {
|
||||||
mChartDirty = false;
|
mChartDirty = false;
|
||||||
updateDateInfo(getStartDate(), getEndDate());
|
updateDateInfo(getStartDate(), getEndDate());
|
||||||
createRefreshTask("Visualizing data", getActivity()).execute();
|
if (refreshTask != null && refreshTask.getStatus() != AsyncTask.Status.FINISHED) {
|
||||||
|
refreshTask.cancel(true);
|
||||||
|
}
|
||||||
|
refreshTask = createRefreshTask("Visualizing data", getActivity()).execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,7 +454,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
colors.add(akActivity.color);
|
colors.add(akActivity.color);
|
||||||
}
|
}
|
||||||
activityEntries.add(createBarEntry(value, i));
|
activityEntries.add(createBarEntry(value, i));
|
||||||
if (hr) {
|
if (hr && isValidHeartRateValue(sample.getCustomValue())) {
|
||||||
heartrateEntries.add(createLineEntry(sample.getCustomValue(), i));
|
heartrateEntries.add(createLineEntry(sample.getCustomValue(), i));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +497,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
barData.setGroupSpace(0);
|
barData.setGroupSpace(0);
|
||||||
combinedData.setData(barData);
|
combinedData.setData(barData);
|
||||||
|
|
||||||
if (hr) {
|
if (hr && heartrateEntries.size() > 0) {
|
||||||
LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate");
|
LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate");
|
||||||
LineData lineData = new LineData(xLabels, heartrateSet);
|
LineData lineData = new LineData(xLabels, heartrateSet);
|
||||||
combinedData.setData(lineData);
|
combinedData.setData(lineData);
|
||||||
@ -507,6 +516,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isValidHeartRateValue(int value) {
|
||||||
|
return value > 0 && value < 255;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this to supply the samples to be displayed.
|
* Implement this to supply the samples to be displayed.
|
||||||
*
|
*
|
||||||
@ -550,14 +563,19 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
|||||||
LineDataSet set1 = new LineDataSet(values, label);
|
LineDataSet set1 = new LineDataSet(values, label);
|
||||||
set1.setColor(HEARTRATE_COLOR);
|
set1.setColor(HEARTRATE_COLOR);
|
||||||
// set1.setColors(colors);
|
// set1.setColors(colors);
|
||||||
// set1.setDrawCubic(true);
|
set1.setDrawCubic(true);
|
||||||
// set1.setCubicIntensity(0.2f);
|
set1.setCubicIntensity(0.1f);
|
||||||
// //set1.setDrawFilled(true);
|
// //set1.setDrawFilled(true);
|
||||||
// set1.setDrawCircles(false);
|
// set1.setDrawCircles(false);
|
||||||
set1.setLineWidth(2f);
|
// set1.setLineWidth(2f);
|
||||||
// set1.setCircleSize(5f);
|
|
||||||
|
set1.setDrawCircles(false);
|
||||||
|
// set1.setCircleRadius(2f);
|
||||||
|
// set1.setDrawFilled(true);
|
||||||
|
|
||||||
|
set1.setLineWidth(0.8f);
|
||||||
// set1.setFillColor(ColorTemplate.getHoloBlue());
|
// set1.setFillColor(ColorTemplate.getHoloBlue());
|
||||||
set1.setDrawValues(false);
|
set1.setDrawValues(true);
|
||||||
// set1.setHighLightColor(Color.rgb(128, 0, 255));
|
// set1.setHighLightColor(Color.rgb(128, 0, 255));
|
||||||
// set1.setColor(Color.rgb(89, 178, 44));
|
// set1.setColor(Color.rgb(89, 178, 44));
|
||||||
set1.setValueTextColor(CHART_TEXT_COLOR);
|
set1.setValueTextColor(CHART_TEXT_COLOR);
|
||||||
|
@ -70,6 +70,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
|||||||
// y.setDrawLabels(false);
|
// y.setDrawLabels(false);
|
||||||
// TODO: make fixed max value optional
|
// TODO: make fixed max value optional
|
||||||
y.setAxisMaxValue(1f);
|
y.setAxisMaxValue(1f);
|
||||||
|
y.setAxisMinValue(0);
|
||||||
y.setDrawTopYLabelEntry(false);
|
y.setDrawTopYLabelEntry(false);
|
||||||
y.setTextColor(CHART_TEXT_COLOR);
|
y.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
|
||||||
@ -82,6 +83,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
|||||||
yAxisRight.setDrawLabels(true);
|
yAxisRight.setDrawLabels(true);
|
||||||
yAxisRight.setDrawTopYLabelEntry(true);
|
yAxisRight.setDrawTopYLabelEntry(true);
|
||||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
yAxisRight.setAxisMaxValue(250);
|
||||||
|
yAxisRight.setAxisMinValue(0);
|
||||||
|
|
||||||
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
||||||
refresh();
|
refresh();
|
||||||
@ -125,6 +128,10 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
|||||||
legendLabels.add(akDeepSleep.label);
|
legendLabels.add(akDeepSleep.label);
|
||||||
legendColors.add(akNotWorn.color);
|
legendColors.add(akNotWorn.color);
|
||||||
legendLabels.add(akNotWorn.label);
|
legendLabels.add(akNotWorn.label);
|
||||||
|
if (supportsHeartrate()) {
|
||||||
|
legendColors.add(HEARTRATE_COLOR);
|
||||||
|
legendLabels.add(HEARTRATE_LABEL);
|
||||||
|
}
|
||||||
chart.getLegend().setCustom(legendColors, legendLabels);
|
chart.getLegend().setCustom(legendColors, legendLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,7 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||||||
// y.setDrawLabels(false);
|
// y.setDrawLabels(false);
|
||||||
// TODO: make fixed max value optional
|
// TODO: make fixed max value optional
|
||||||
y.setAxisMaxValue(1f);
|
y.setAxisMaxValue(1f);
|
||||||
|
y.setAxisMinValue(0);
|
||||||
y.setDrawTopYLabelEntry(false);
|
y.setDrawTopYLabelEntry(false);
|
||||||
y.setTextColor(CHART_TEXT_COLOR);
|
y.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
|
||||||
@ -159,10 +160,12 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||||||
|
|
||||||
YAxis yAxisRight = mActivityChart.getAxisRight();
|
YAxis yAxisRight = mActivityChart.getAxisRight();
|
||||||
yAxisRight.setDrawGridLines(false);
|
yAxisRight.setDrawGridLines(false);
|
||||||
yAxisRight.setEnabled(false);
|
yAxisRight.setEnabled(supportsHeartrate());
|
||||||
yAxisRight.setDrawLabels(false);
|
yAxisRight.setDrawLabels(true);
|
||||||
yAxisRight.setDrawTopYLabelEntry(false);
|
yAxisRight.setDrawTopYLabelEntry(true);
|
||||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||||
|
yAxisRight.setAxisMaxValue(250);
|
||||||
|
yAxisRight.setAxisMinValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupLegend(Chart chart) {
|
protected void setupLegend(Chart chart) {
|
||||||
@ -172,6 +175,10 @@ public class SleepChartFragment extends AbstractChartFragment {
|
|||||||
legendLabels.add(akLightSleep.label);
|
legendLabels.add(akLightSleep.label);
|
||||||
legendColors.add(akDeepSleep.color);
|
legendColors.add(akDeepSleep.color);
|
||||||
legendLabels.add(akDeepSleep.label);
|
legendLabels.add(akDeepSleep.label);
|
||||||
|
if (supportsHeartrate()) {
|
||||||
|
legendColors.add(HEARTRATE_COLOR);
|
||||||
|
legendLabels.add(HEARTRATE_LABEL);
|
||||||
|
}
|
||||||
chart.getLegend().setCustom(legendColors, legendLabels);
|
chart.getLegend().setCustom(legendColors, legendLabels);
|
||||||
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
|||||||
*/
|
*/
|
||||||
public class ItemWithDetailsAdapter extends ArrayAdapter<ItemWithDetails> {
|
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 final Context context;
|
||||||
private boolean horizontalAlignment;
|
private boolean horizontalAlignment;
|
||||||
|
private int size = SIZE_MEDIUM;
|
||||||
|
|
||||||
public ItemWithDetailsAdapter(Context context, List<ItemWithDetails> items) {
|
public ItemWithDetailsAdapter(Context context, List<ItemWithDetails> items) {
|
||||||
super(context, 0, items);
|
super(context, 0, items);
|
||||||
@ -42,7 +46,14 @@ public class ItemWithDetailsAdapter extends ArrayAdapter<ItemWithDetails> {
|
|||||||
if (horizontalAlignment) {
|
if (horizontalAlignment) {
|
||||||
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
|
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
|
||||||
} else {
|
} 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);
|
view = inflater.inflate(R.layout.item_with_details, parent, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
|
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
|
||||||
@ -55,4 +66,12 @@ public class ItemWithDetailsAdapter extends ArrayAdapter<ItemWithDetails> {
|
|||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSize(int size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +275,24 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind, SampleProvider provider) {
|
||||||
|
try (SQLiteDatabase db = this.getReadableDatabase()) {
|
||||||
|
String sql = "UPDATE " + TABLE_GBACTIVITYSAMPLES + " SET " + KEY_TYPE + "= ? WHERE "
|
||||||
|
+ KEY_TYPE + " = ? AND "
|
||||||
|
+ KEY_PROVIDER + " = ? AND "
|
||||||
|
+ KEY_TIMESTAMP + " >= ? AND " + KEY_TIMESTAMP + " < ? ;"; //do not use BETWEEN because the range is inclusive in that case!
|
||||||
|
|
||||||
|
SQLiteStatement statement = db.compileStatement(sql);
|
||||||
|
statement.bindLong(1, toKind);
|
||||||
|
statement.bindLong(2, fromKind);
|
||||||
|
statement.bindLong(3, provider.getID());
|
||||||
|
statement.bindLong(4, timestampFrom);
|
||||||
|
statement.bindLong(5, timestampTo);
|
||||||
|
statement.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int fetchLatestTimestamp(SampleProvider provider) {
|
public int fetchLatestTimestamp(SampleProvider provider) {
|
||||||
try (SQLiteDatabase db = this.getReadableDatabase()) {
|
try (SQLiteDatabase db = this.getReadableDatabase()) {
|
||||||
|
@ -32,6 +32,8 @@ public interface DBHandler {
|
|||||||
|
|
||||||
void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind, SampleProvider provider);
|
void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind, SampleProvider provider);
|
||||||
|
|
||||||
|
void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind, SampleProvider provider);
|
||||||
|
|
||||||
int fetchLatestTimestamp(SampleProvider provider);
|
int fetchLatestTimestamp(SampleProvider provider);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||||
|
|
||||||
|
public class GBDeviceEventDisplayMessage {
|
||||||
|
public String message;
|
||||||
|
public int duration;
|
||||||
|
public int severity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event for displaying a message to the user. How the message is displayed
|
||||||
|
* is a detail of the current activity, which needs to listen to the Intent
|
||||||
|
* GB.ACTION_DISPLAY_MESSAGE.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param duration
|
||||||
|
* @param severity
|
||||||
|
*/
|
||||||
|
public GBDeviceEventDisplayMessage(String message, int duration, int severity) {
|
||||||
|
this.message = message;
|
||||||
|
this.duration = duration;
|
||||||
|
this.severity = severity;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.devices;
|
package nodomain.freeyourgadget.gadgetbridge.devices;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies all events that GadgetBridge intends to send to the gadget device.
|
* Specifies all events that GadgetBridge intends to send to the gadget device.
|
||||||
@ -22,9 +22,9 @@ public interface EventHandler {
|
|||||||
|
|
||||||
void onSetAlarms(ArrayList<? extends Alarm> alarms);
|
void onSetAlarms(ArrayList<? extends Alarm> alarms);
|
||||||
|
|
||||||
void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command);
|
void onSetCallState(CallSpec callSpec);
|
||||||
|
|
||||||
void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr);
|
void onSetMusicInfo(MusicSpec musicSpec);
|
||||||
|
|
||||||
void onEnableRealtimeSteps(boolean enable);
|
void onEnableRealtimeSteps(boolean enable);
|
||||||
|
|
||||||
@ -48,4 +48,5 @@ public interface EventHandler {
|
|||||||
|
|
||||||
void onScreenshotReq();
|
void onScreenshotReq();
|
||||||
|
|
||||||
|
void onEnableHeartRateSleepSupport(boolean enable);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public interface InstallHandler {
|
|||||||
void validateInstallation(InstallActivity installActivity, GBDevice device);
|
void validateInstallation(InstallActivity installActivity, GBDevice device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows device specivic code to be execute just before the installation starts
|
* Allows device specific code to be executed just before the installation starts
|
||||||
*/
|
*/
|
||||||
void onStartInstall(GBDevice device);
|
void onStartInstall(GBDevice device);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ public final class MiBandConst {
|
|||||||
public static final String PREF_MIBAND_FITNESS_GOAL = "mi_fitness_goal";
|
public static final String PREF_MIBAND_FITNESS_GOAL = "mi_fitness_goal";
|
||||||
public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer";
|
public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer";
|
||||||
public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar";
|
public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar";
|
||||||
|
public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection";
|
||||||
|
|
||||||
|
|
||||||
public static final String ORIGIN_SMS = "sms";
|
public static final String ORIGIN_SMS = "sms";
|
||||||
|
@ -135,6 +135,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean getHeartrateSleepSupport(String miBandAddress) throws IllegalArgumentException {
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext());
|
||||||
|
return prefs.getBoolean(MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, false);
|
||||||
|
}
|
||||||
|
|
||||||
public static int getFitnessGoal(String miBandAddress) throws IllegalArgumentException {
|
public static int getFitnessGoal(String miBandAddress) throws IllegalArgumentException {
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext());
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext());
|
||||||
return Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_FITNESS_GOAL, "10000"));
|
return Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_FITNESS_GOAL, "10000"));
|
||||||
|
@ -5,6 +5,7 @@ import android.os.Bundle;
|
|||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractSettingsActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractSettingsActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter;
|
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter;
|
||||||
@ -18,6 +19,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DONT_ACK_TRANSFER;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DONT_ACK_TRANSFER;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_WEARSIDE;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_WEARSIDE;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT;
|
||||||
@ -43,6 +45,14 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final Preference enableHeartrateSleepSupport = findPreference(PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION);
|
||||||
|
enableHeartrateSleepSupport.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||||
|
GBApplication.deviceService().onEnableHeartRateSleepSupport(Boolean.TRUE.equals(newVal));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,12 +8,11 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
|
|
||||||
public class MusicPlaybackReceiver extends BroadcastReceiver {
|
public class MusicPlaybackReceiver extends BroadcastReceiver {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MusicPlaybackReceiver.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MusicPlaybackReceiver.class);
|
||||||
|
|
||||||
private static String mLastSource;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String artist = intent.getStringExtra("artist");
|
String artist = intent.getStringExtra("artist");
|
||||||
@ -24,11 +23,16 @@ public class MusicPlaybackReceiver extends BroadcastReceiver {
|
|||||||
for (String key : bundle.keySet()) {
|
for (String key : bundle.keySet()) {
|
||||||
Object value = bundle.get(key);
|
Object value = bundle.get(key);
|
||||||
LOG.info(String.format("%s %s (%s)", key,
|
LOG.info(String.format("%s %s (%s)", key,
|
||||||
value.toString(), value.getClass().getName()));
|
value != null ? value.toString() : "null", value != null ? value.getClass().getName() : "no class"));
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
LOG.info("Current track: " + artist + ", " + album + ", " + track);
|
LOG.info("Current track: " + artist + ", " + album + ", " + track);
|
||||||
|
|
||||||
GBApplication.deviceService().onSetMusicInfo(artist, album, track, 0, 0, 0);
|
MusicSpec musicSpec = new MusicSpec();
|
||||||
|
musicSpec.artist = artist;
|
||||||
|
musicSpec.artist = album;
|
||||||
|
musicSpec.artist = track;
|
||||||
|
|
||||||
|
GBApplication.deviceService().onSetMusicInfo(musicSpec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,9 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
case GBApplication.ACTION_QUIT:
|
||||||
|
stopSelf();
|
||||||
|
break;
|
||||||
case ACTION_MUTE:
|
case ACTION_MUTE:
|
||||||
case ACTION_OPEN: {
|
case ACTION_OPEN: {
|
||||||
StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications();
|
StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications();
|
||||||
@ -130,6 +133,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
IntentFilter filterLocal = new IntentFilter();
|
IntentFilter filterLocal = new IntentFilter();
|
||||||
|
filterLocal.addAction(GBApplication.ACTION_QUIT);
|
||||||
filterLocal.addAction(ACTION_OPEN);
|
filterLocal.addAction(ACTION_OPEN);
|
||||||
filterLocal.addAction(ACTION_DISMISS);
|
filterLocal.addAction(ACTION_DISMISS);
|
||||||
filterLocal.addAction(ACTION_DISMISS_ALL);
|
filterLocal.addAction(ACTION_DISMISS_ALL);
|
||||||
|
@ -8,7 +8,7 @@ import android.preference.PreferenceManager;
|
|||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
|
||||||
|
|
||||||
public class PhoneCallReceiver extends BroadcastReceiver {
|
public class PhoneCallReceiver extends BroadcastReceiver {
|
||||||
@ -40,35 +40,38 @@ public class PhoneCallReceiver extends BroadcastReceiver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceCommand callCommand = null;
|
int callCommand = CallSpec.CALL_UNDEFINED;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case TelephonyManager.CALL_STATE_RINGING:
|
case TelephonyManager.CALL_STATE_RINGING:
|
||||||
mSavedNumber = number;
|
mSavedNumber = number;
|
||||||
callCommand = ServiceCommand.CALL_INCOMING;
|
callCommand = CallSpec.CALL_INCOMING;
|
||||||
break;
|
break;
|
||||||
case TelephonyManager.CALL_STATE_OFFHOOK:
|
case TelephonyManager.CALL_STATE_OFFHOOK:
|
||||||
if (mLastState == TelephonyManager.CALL_STATE_RINGING) {
|
if (mLastState == TelephonyManager.CALL_STATE_RINGING) {
|
||||||
callCommand = ServiceCommand.CALL_START;
|
callCommand = CallSpec.CALL_START;
|
||||||
} else {
|
} else {
|
||||||
callCommand = ServiceCommand.CALL_OUTGOING;
|
callCommand = CallSpec.CALL_OUTGOING;
|
||||||
mSavedNumber = number;
|
mSavedNumber = number;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TelephonyManager.CALL_STATE_IDLE:
|
case TelephonyManager.CALL_STATE_IDLE:
|
||||||
if (mLastState == TelephonyManager.CALL_STATE_RINGING) {
|
if (mLastState == TelephonyManager.CALL_STATE_RINGING) {
|
||||||
//missed call would be correct here
|
//missed call would be correct here
|
||||||
callCommand = ServiceCommand.CALL_END;
|
callCommand = CallSpec.CALL_END;
|
||||||
} else {
|
} else {
|
||||||
callCommand = ServiceCommand.CALL_END;
|
callCommand = CallSpec.CALL_END;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (callCommand != null) {
|
if (callCommand != CallSpec.CALL_UNDEFINED) {
|
||||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
if ("never".equals(sharedPrefs.getString("notification_mode_calls", "always"))) {
|
if ("never".equals(sharedPrefs.getString("notification_mode_calls", "always"))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GBApplication.deviceService().onSetCallState(mSavedNumber, null, callCommand);
|
CallSpec callSpec = new CallSpec();
|
||||||
|
callSpec.number = mSavedNumber;
|
||||||
|
callSpec.command = callCommand;
|
||||||
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
}
|
}
|
||||||
mLastState = state;
|
mLastState = state;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ public class GBDevice implements Parcelable {
|
|||||||
private BatteryState mBatteryState;
|
private BatteryState mBatteryState;
|
||||||
private short mRssi = RSSI_UNKNOWN;
|
private short mRssi = RSSI_UNKNOWN;
|
||||||
private String mBusyTask;
|
private String mBusyTask;
|
||||||
private String infoString;
|
|
||||||
private List<ItemWithDetails> mDeviceInfos;
|
private List<ItemWithDetails> mDeviceInfos;
|
||||||
|
|
||||||
public GBDevice(String address, String name, DeviceType deviceType) {
|
public GBDevice(String address, String name, DeviceType deviceType) {
|
||||||
@ -204,23 +203,25 @@ public class GBDevice implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getStateString() {
|
public String getStateString() {
|
||||||
|
/*
|
||||||
|
* for simplicity the user wont see all internal states, just connecting -> connected
|
||||||
|
* instead of connecting->connected->initializing->initialized
|
||||||
|
*/
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case NOT_CONNECTED:
|
case NOT_CONNECTED:
|
||||||
return GBApplication.getContext().getString(R.string.not_connected);
|
return GBApplication.getContext().getString(R.string.not_connected);
|
||||||
case WAITING_FOR_RECONNECT:
|
case WAITING_FOR_RECONNECT:
|
||||||
return GBApplication.getContext().getString(R.string.waiting_for_reconnect);
|
return GBApplication.getContext().getString(R.string.waiting_for_reconnect);
|
||||||
case CONNECTING:
|
case CONNECTING:
|
||||||
return GBApplication.getContext().getString(R.string.connecting);
|
|
||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
return GBApplication.getContext().getString(R.string.connected);
|
|
||||||
case INITIALIZING:
|
case INITIALIZING:
|
||||||
return GBApplication.getContext().getString(R.string.initializing);
|
return GBApplication.getContext().getString(R.string.connecting);
|
||||||
case AUTHENTICATION_REQUIRED:
|
case AUTHENTICATION_REQUIRED:
|
||||||
return GBApplication.getContext().getString(R.string.authentication_required);
|
return GBApplication.getContext().getString(R.string.authentication_required);
|
||||||
case AUTHENTICATING:
|
case AUTHENTICATING:
|
||||||
return GBApplication.getContext().getString(R.string.authenticating);
|
return GBApplication.getContext().getString(R.string.authenticating);
|
||||||
case INITIALIZED:
|
case INITIALIZED:
|
||||||
return GBApplication.getContext().getString(R.string.initialized);
|
return GBApplication.getContext().getString(R.string.connected);
|
||||||
}
|
}
|
||||||
return GBApplication.getContext().getString(R.string.unknown_state);
|
return GBApplication.getContext().getString(R.string.unknown_state);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||||
|
|
||||||
public class GBDeviceService implements DeviceService {
|
public class GBDeviceService implements DeviceService {
|
||||||
@ -115,23 +116,23 @@ public class GBDeviceService implements DeviceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(String number, String name, ServiceCommand command) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
// name is actually ignored and provided by the service itself...
|
// name is actually ignored and provided by the service itself...
|
||||||
Intent intent = createIntent().setAction(ACTION_CALLSTATE)
|
Intent intent = createIntent().setAction(ACTION_CALLSTATE)
|
||||||
.putExtra(EXTRA_CALL_PHONENUMBER, number)
|
.putExtra(EXTRA_CALL_PHONENUMBER, callSpec.number)
|
||||||
.putExtra(EXTRA_CALL_COMMAND, command);
|
.putExtra(EXTRA_CALL_COMMAND, callSpec.command);
|
||||||
invokeService(intent);
|
invokeService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
Intent intent = createIntent().setAction(ACTION_SETMUSICINFO)
|
Intent intent = createIntent().setAction(ACTION_SETMUSICINFO)
|
||||||
.putExtra(EXTRA_MUSIC_ARTIST, artist)
|
.putExtra(EXTRA_MUSIC_ARTIST, musicSpec.artist)
|
||||||
.putExtra(EXTRA_MUSIC_ALBUM, album)
|
.putExtra(EXTRA_MUSIC_ALBUM, musicSpec.album)
|
||||||
.putExtra(EXTRA_MUSIC_TRACK, track)
|
.putExtra(EXTRA_MUSIC_TRACK, musicSpec.track)
|
||||||
.putExtra(EXTRA_MUSIC_DURATION, duration)
|
.putExtra(EXTRA_MUSIC_DURATION, musicSpec.duration)
|
||||||
.putExtra(EXTRA_MUSIC_TRACKCOUNT, trackCount)
|
.putExtra(EXTRA_MUSIC_TRACKCOUNT, musicSpec.trackCount)
|
||||||
.putExtra(EXTRA_MUSIC_TRACKNR, trackNr);
|
.putExtra(EXTRA_MUSIC_TRACKNR, musicSpec.trackNr);
|
||||||
invokeService(intent);
|
invokeService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +206,14 @@ public class GBDeviceService implements DeviceService {
|
|||||||
@Override
|
@Override
|
||||||
public void onEnableRealtimeSteps(boolean enable) {
|
public void onEnableRealtimeSteps(boolean enable) {
|
||||||
Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_STEPS)
|
Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_STEPS)
|
||||||
.putExtra(EXTRA_ENABLE_REALTIME_STEPS, enable);
|
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||||
|
invokeService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
Intent intent = createIntent().setAction(ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT)
|
||||||
|
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
|
||||||
invokeService(intent);
|
invokeService(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||||
|
|
||||||
|
public class CallSpec {
|
||||||
|
public static final int CALL_UNDEFINED = 1;
|
||||||
|
public static final int CALL_ACCEPT = 1;
|
||||||
|
public static final int CALL_INCOMING = 2;
|
||||||
|
public static final int CALL_OUTGOING = 3;
|
||||||
|
public static final int CALL_REJECT = 4;
|
||||||
|
public static final int CALL_START = 5;
|
||||||
|
public static final int CALL_END = 6;
|
||||||
|
|
||||||
|
public String number;
|
||||||
|
public String name;
|
||||||
|
public int command;
|
||||||
|
}
|
@ -33,6 +33,7 @@ public interface DeviceService extends EventHandler {
|
|||||||
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
|
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
|
||||||
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
||||||
String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps";
|
String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps";
|
||||||
|
String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support";
|
||||||
String EXTRA_DEVICE_ADDRESS = "device_address";
|
String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||||
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
||||||
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
||||||
@ -58,7 +59,7 @@ public interface DeviceService extends EventHandler {
|
|||||||
String EXTRA_URI = "uri";
|
String EXTRA_URI = "uri";
|
||||||
String EXTRA_ALARMS = "alarms";
|
String EXTRA_ALARMS = "alarms";
|
||||||
String EXTRA_PERFORM_PAIR = "perform_pair";
|
String EXTRA_PERFORM_PAIR = "perform_pair";
|
||||||
String EXTRA_ENABLE_REALTIME_STEPS = "enable_realtime_steps";
|
String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps";
|
||||||
String EXTRA_REALTIME_STEPS = "realtime_steps";
|
String EXTRA_REALTIME_STEPS = "realtime_steps";
|
||||||
String EXTRA_TIMESTAMP = "timestamp";
|
String EXTRA_TIMESTAMP = "timestamp";
|
||||||
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||||
|
|
||||||
|
public class MusicSpec {
|
||||||
|
public static final int MUSIC_UNDEFINED = 0;
|
||||||
|
public static final int MUSIC_PLAY = 1;
|
||||||
|
public static final int MUSIC_PAUSE = 2;
|
||||||
|
public static final int MUSIC_PLAYPAUSE = 3;
|
||||||
|
public static final int MUSIC_NEXT = 4;
|
||||||
|
public static final int MUSIC_PREVIOUS = 5;
|
||||||
|
|
||||||
|
public String artist;
|
||||||
|
public String album;
|
||||||
|
public String track;
|
||||||
|
public int duration;
|
||||||
|
public int trackCount;
|
||||||
|
public int trackNr;
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
|
||||||
|
|
||||||
public enum ServiceCommand {
|
|
||||||
|
|
||||||
UNDEFINED,
|
|
||||||
|
|
||||||
CALL_ACCEPT,
|
|
||||||
CALL_END,
|
|
||||||
CALL_INCOMING,
|
|
||||||
CALL_OUTGOING,
|
|
||||||
CALL_REJECT,
|
|
||||||
CALL_START,
|
|
||||||
|
|
||||||
MUSIC_PLAY,
|
|
||||||
MUSIC_PAUSE,
|
|
||||||
MUSIC_PLAYPAUSE,
|
|
||||||
MUSIC_NEXT,
|
|
||||||
MUSIC_PREVIOUS,
|
|
||||||
|
|
||||||
APP_INFO_NAME,
|
|
||||||
VERSION_FIRMWARE
|
|
||||||
}
|
|
@ -32,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
||||||
@ -280,4 +281,14 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
|||||||
gbDevice.sendDeviceUpdateIntent(context);
|
gbDevice.sendDeviceUpdateIntent(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleGBDeviceEvent(GBDeviceEventDisplayMessage message) {
|
||||||
|
GB.log(message.message, message.severity, null);
|
||||||
|
|
||||||
|
Intent messageIntent = new Intent(GB.ACTION_DISPLAY_MESSAGE);
|
||||||
|
messageIntent.putExtra(GB.DISPLAY_MESSAGE_MESSAGE, message.message);
|
||||||
|
messageIntent.putExtra(GB.DISPLAY_MESSAGE_DURATION, message.duration);
|
||||||
|
messageIntent.putExtra(GB.DISPLAY_MESSAGE_SEVERITY, message.severity);
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(messageIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,10 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.SMSReceiver;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.TimeChangeReceiver;
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.TimeChangeReceiver;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CA
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP;
|
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_DISCONNECT;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS;
|
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_FETCH_ACTIVITY_DATA;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE;
|
||||||
@ -63,10 +65,10 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALA
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOOLEAN_ENABLE;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ENABLE_REALTIME_STEPS;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
|
||||||
@ -277,26 +279,32 @@ public class DeviceCommunicationService extends Service {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACTION_CALLSTATE:
|
case ACTION_CALLSTATE:
|
||||||
ServiceCommand command = (ServiceCommand) intent.getSerializableExtra(EXTRA_CALL_COMMAND);
|
int command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED);
|
||||||
|
|
||||||
String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER);
|
String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER);
|
||||||
String callerName = null;
|
String callerName = null;
|
||||||
if (phoneNumber != null) {
|
if (phoneNumber != null) {
|
||||||
callerName = getContactDisplayNameByNumber(phoneNumber);
|
callerName = getContactDisplayNameByNumber(phoneNumber);
|
||||||
}
|
}
|
||||||
mDeviceSupport.onSetCallState(phoneNumber, callerName, command);
|
|
||||||
|
CallSpec callSpec = new CallSpec();
|
||||||
|
callSpec.command = command;
|
||||||
|
callSpec.number = phoneNumber;
|
||||||
|
callSpec.name = callerName;
|
||||||
|
mDeviceSupport.onSetCallState(callSpec);
|
||||||
break;
|
break;
|
||||||
case ACTION_SETTIME:
|
case ACTION_SETTIME:
|
||||||
mDeviceSupport.onSetTime();
|
mDeviceSupport.onSetTime();
|
||||||
break;
|
break;
|
||||||
case ACTION_SETMUSICINFO:
|
case ACTION_SETMUSICINFO:
|
||||||
String artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST);
|
MusicSpec musicSpec = new MusicSpec();
|
||||||
String album = intent.getStringExtra(EXTRA_MUSIC_ALBUM);
|
musicSpec.artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST);
|
||||||
String track = intent.getStringExtra(EXTRA_MUSIC_TRACK);
|
musicSpec.album = intent.getStringExtra(EXTRA_MUSIC_ALBUM);
|
||||||
int duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0);
|
musicSpec.track = intent.getStringExtra(EXTRA_MUSIC_TRACK);
|
||||||
int trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0);
|
musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0);
|
||||||
int trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0);
|
musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0);
|
||||||
mDeviceSupport.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr);
|
musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0);
|
||||||
|
mDeviceSupport.onSetMusicInfo(musicSpec);
|
||||||
break;
|
break;
|
||||||
case ACTION_REQUEST_APPINFO:
|
case ACTION_REQUEST_APPINFO:
|
||||||
mDeviceSupport.onAppInfoReq();
|
mDeviceSupport.onAppInfoReq();
|
||||||
@ -331,11 +339,17 @@ public class DeviceCommunicationService extends Service {
|
|||||||
ArrayList<Alarm> alarms = intent.getParcelableArrayListExtra(EXTRA_ALARMS);
|
ArrayList<Alarm> alarms = intent.getParcelableArrayListExtra(EXTRA_ALARMS);
|
||||||
mDeviceSupport.onSetAlarms(alarms);
|
mDeviceSupport.onSetAlarms(alarms);
|
||||||
break;
|
break;
|
||||||
case ACTION_ENABLE_REALTIME_STEPS:
|
case ACTION_ENABLE_REALTIME_STEPS: {
|
||||||
boolean enable = intent.getBooleanExtra(EXTRA_ENABLE_REALTIME_STEPS, false);
|
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
|
||||||
mDeviceSupport.onEnableRealtimeSteps(enable);
|
mDeviceSupport.onEnableRealtimeSteps(enable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT: {
|
||||||
|
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
|
||||||
|
mDeviceSupport.onEnableHeartRateSleepSupport(enable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
@ -417,7 +431,10 @@ public class DeviceCommunicationService extends Service {
|
|||||||
}
|
}
|
||||||
if (mMusicPlaybackReceiver == null) {
|
if (mMusicPlaybackReceiver == null) {
|
||||||
mMusicPlaybackReceiver = new MusicPlaybackReceiver();
|
mMusicPlaybackReceiver = new MusicPlaybackReceiver();
|
||||||
registerReceiver(mMusicPlaybackReceiver, new IntentFilter("com.android.music.metachanged"));
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction("com.android.music.metachanged");
|
||||||
|
//filter.addAction("com.android.music.playstatechanged");
|
||||||
|
registerReceiver(mMusicPlaybackReceiver, filter);
|
||||||
}
|
}
|
||||||
if (mTimeChangeReceiver == null) {
|
if (mTimeChangeReceiver == null) {
|
||||||
mTimeChangeReceiver = new TimeChangeReceiver();
|
mTimeChangeReceiver = new TimeChangeReceiver();
|
||||||
|
@ -13,8 +13,9 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps another device support instance and supports busy-checking and throttling of events.
|
* Wraps another device support instance and supports busy-checking and throttling of events.
|
||||||
@ -131,19 +132,19 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
|||||||
// No throttling for the other events
|
// No throttling for the other events
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(String number, String name, ServiceCommand command) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
if (checkBusy("set call state")) {
|
if (checkBusy("set call state")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delegate.onSetCallState(number, name, command);
|
delegate.onSetCallState(callSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
if (checkBusy("set music info")) {
|
if (checkBusy("set music info")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delegate.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr);
|
delegate.onSetMusicInfo(musicSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -241,4 +242,12 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
|||||||
}
|
}
|
||||||
delegate.onEnableRealtimeSteps(enable);
|
delegate.onEnableRealtimeSteps(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
if (checkBusy("enable heartrate sleep support: " + enable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delegate.onEnableHeartRateSleepSupport(enable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import java.util.Set;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka
|
* Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka
|
||||||
|
@ -113,6 +113,10 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
|
|||||||
return operationStatus == OperationStatus.RUNNING;
|
return operationStatus == OperationStatus.RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOperationFinished() {
|
||||||
|
return operationStatus == OperationStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
public T getSupport() {
|
public T getSupport() {
|
||||||
return mSupport;
|
return mSupport;
|
||||||
}
|
}
|
||||||
|
@ -157,11 +157,12 @@ public final class BtLEQueue {
|
|||||||
LOG.info("Attempting to connect to " + mGbDevice.getName());
|
LOG.info("Attempting to connect to " + mGbDevice.getName());
|
||||||
mBluetoothAdapter.cancelDiscovery();
|
mBluetoothAdapter.cancelDiscovery();
|
||||||
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress());
|
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress());
|
||||||
|
boolean result;
|
||||||
synchronized (mGattMonitor) {
|
synchronized (mGattMonitor) {
|
||||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback);
|
mBluetoothGatt = remoteDevice.connectGatt(mContext, true, internalGattCallback);
|
||||||
// result = mBluetoothGatt.connect();
|
result = mBluetoothGatt.connect();
|
||||||
}
|
}
|
||||||
boolean result = mBluetoothGatt != null;
|
// boolean result = mBluetoothGatt != null;
|
||||||
if (result) {
|
if (result) {
|
||||||
setDeviceConnectionState(State.CONNECTING);
|
setDeviceConnectionState(State.CONNECTING);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special action that is executed at the very front of the initialization
|
* A special action that is executed at the very front of the initialization
|
@ -0,0 +1,30 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
|
||||||
|
public abstract class ConditionalWriteAction extends WriteAction {
|
||||||
|
public ConditionalWriteAction(BluetoothGattCharacteristic characteristic) {
|
||||||
|
super(characteristic, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
|
||||||
|
byte[] conditionalValue = checkCondition();
|
||||||
|
if (conditionalValue != null) {
|
||||||
|
return super.writeValue(gatt, characteristic, conditionalValue);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the condition whether the write shall happen or not.
|
||||||
|
* Returns the actual value to be written or null in case nothing shall be written.
|
||||||
|
* <p/>
|
||||||
|
* Note that returning null will not cause run() to return false, in other words,
|
||||||
|
* the rest of the queue will still be executed.
|
||||||
|
*
|
||||||
|
* @return the value to be written or null to not write anything
|
||||||
|
*/
|
||||||
|
protected abstract byte[] checkCondition();
|
||||||
|
}
|
@ -22,16 +22,26 @@ public class WriteAction extends BtLEAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean run(BluetoothGatt gatt) {
|
public boolean run(BluetoothGatt gatt) {
|
||||||
int properties = getCharacteristic().getProperties();
|
BluetoothGattCharacteristic characteristic = getCharacteristic();
|
||||||
|
int properties = characteristic.getProperties();
|
||||||
//TODO: expectsResult should return false if PROPERTY_WRITE_NO_RESPONSE is true, but this yelds to timing issues
|
//TODO: expectsResult should return false if PROPERTY_WRITE_NO_RESPONSE is true, but this yelds to timing issues
|
||||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0)) {
|
if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0)) {
|
||||||
if (getCharacteristic().setValue(value)) {
|
return writeValue(gatt, characteristic, value);
|
||||||
return gatt.writeCharacteristic(getCharacteristic());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
|
||||||
|
if (characteristic.setValue(value)) {
|
||||||
|
return gatt.writeCharacteristic(characteristic);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final byte[] getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean expectsResult() {
|
public boolean expectsResult() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothGatt;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
||||||
|
|
||||||
public class CheckAuthenticationNeededAction extends PlainAction {
|
public class CheckAuthenticationNeededAction extends AbortTransactionAction {
|
||||||
private final GBDevice mDevice;
|
private final GBDevice mDevice;
|
||||||
|
|
||||||
public CheckAuthenticationNeededAction(GBDevice device) {
|
public CheckAuthenticationNeededAction(GBDevice device) {
|
||||||
@ -14,14 +12,14 @@ public class CheckAuthenticationNeededAction extends PlainAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean run(BluetoothGatt gatt) {
|
protected boolean shouldAbort() {
|
||||||
// the state is set in MiBandSupport.handleNotificationNotif()
|
// the state is set in MiBandSupport.handleNotificationNotif()
|
||||||
switch (mDevice.getState()) {
|
switch (mDevice.getState()) {
|
||||||
case AUTHENTICATION_REQUIRED: // fall through
|
case AUTHENTICATION_REQUIRED: // fall through
|
||||||
case AUTHENTICATING:
|
case AUTHENTICATING:
|
||||||
return false; // abort the whole thing
|
return true; // abort the whole thing
|
||||||
default:
|
default:
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ public class DeviceInfo extends AbstractInfo {
|
|||||||
|
|
||||||
public boolean isMili1S() {
|
public boolean isMili1S() {
|
||||||
// TODO: this is probably not quite correct, but hopefully sufficient for early 1S support
|
// TODO: this is probably not quite correct, but hopefully sufficient for early 1S support
|
||||||
return feature == 4 && appearance == 0 || feature == 4 && hwVersion == 4;
|
return (feature == 4 && appearance == 0) || hwVersion == 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHwVersion() {
|
public String getHwVersion() {
|
||||||
|
@ -34,16 +34,18 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation;
|
||||||
@ -109,6 +111,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
.sendUserInfo(builder)
|
.sendUserInfo(builder)
|
||||||
.checkAuthenticationNeeded(builder, getDevice())
|
.checkAuthenticationNeeded(builder, getDevice())
|
||||||
.setWearLocation(builder)
|
.setWearLocation(builder)
|
||||||
|
.setHeartrateSleepSupport(builder)
|
||||||
.setFitnessGoal(builder)
|
.setFitnessGoal(builder)
|
||||||
.enableFurtherNotifications(builder, true)
|
.enableFurtherNotifications(builder, true)
|
||||||
.setCurrentTime(builder)
|
.setCurrentTime(builder)
|
||||||
@ -368,6 +371,44 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
try {
|
||||||
|
TransactionBuilder builder = performInitialized("enable heart rate sleep support: " + enable);
|
||||||
|
setHeartrateSleepSupport(builder);
|
||||||
|
builder.queue(getQueue());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Error toggling heart rate sleep support: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of device initialization process. Do not call manually.
|
||||||
|
*
|
||||||
|
* @param builder
|
||||||
|
*/
|
||||||
|
private MiBandSupport setHeartrateSleepSupport(TransactionBuilder builder) {
|
||||||
|
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
|
||||||
|
if (characteristic != null) {
|
||||||
|
builder.add(new ConditionalWriteAction(characteristic) {
|
||||||
|
@Override
|
||||||
|
protected byte[] checkCondition() {
|
||||||
|
if (!supportsHeartRate()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) {
|
||||||
|
LOG.info("Enabling heartrate sleep support...");
|
||||||
|
return startHeartMeasurementSleep;
|
||||||
|
} else {
|
||||||
|
LOG.info("Disabling heartrate sleep support...");
|
||||||
|
return stopHeartMeasurementSleep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) {
|
private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized(task);
|
TransactionBuilder builder = performInitialized(task);
|
||||||
@ -519,8 +560,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(String number, String name, ServiceCommand command) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
if (ServiceCommand.CALL_INCOMING.equals(command)) {
|
if (callSpec.command == CallSpec.CALL_INCOMING) {
|
||||||
telephoneRinging = true;
|
telephoneRinging = true;
|
||||||
AbortTransactionAction abortAction = new AbortTransactionAction() {
|
AbortTransactionAction abortAction = new AbortTransactionAction() {
|
||||||
@Override
|
@Override
|
||||||
@ -529,7 +570,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction);
|
performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction);
|
||||||
} else if (ServiceCommand.CALL_START.equals(command) || ServiceCommand.CALL_END.equals(command)) {
|
} else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) {
|
||||||
telephoneRinging = false;
|
telephoneRinging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,7 +581,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
// not supported
|
// not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,6 +766,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
handleBatteryInfo(characteristic.getValue(), status);
|
handleBatteryInfo(characteristic.getValue(), status);
|
||||||
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||||
logHeartrate(characteristic.getValue());
|
logHeartrate(characteristic.getValue());
|
||||||
|
} else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) {
|
||||||
|
logDate(characteristic.getValue());
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Unhandled characteristic read: " + characteristicUUID);
|
LOG.info("Unhandled characteristic read: " + characteristicUUID);
|
||||||
logMessageContent(characteristic.getValue());
|
logMessageContent(characteristic.getValue());
|
||||||
@ -756,6 +799,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void logDate(byte[] value) {
|
||||||
|
GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value);
|
||||||
|
LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime()));
|
||||||
|
}
|
||||||
|
|
||||||
public void logHeartrate(byte[] value) {
|
public void logHeartrate(byte[] value) {
|
||||||
LOG.info("Got heartrate:");
|
LOG.info("Got heartrate:");
|
||||||
if (value.length == 2 && value[0] == 6) {
|
if (value.length == 2 && value[0] == 6) {
|
||||||
@ -889,7 +937,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||||
LOG.warn("Could not write to the control point.");
|
LOG.warn("Could not write to the control point.");
|
||||||
}
|
}
|
||||||
LOG.info("handleControlPoint write status:" + status);
|
LOG.info("handleControlPoint write status:" + status + "; length: " + (value != null ? value.length : "(null)"));
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
for (byte b : value) {
|
for (byte b : value) {
|
||||||
|
@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGatt;
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ import java.util.Arrays;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
@ -53,13 +55,14 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
|
|
||||||
updateCoordinator.initNextOperation();
|
updateCoordinator.initNextOperation();
|
||||||
if (!updateCoordinator.sendFwInfo()) {
|
if (!updateCoordinator.sendFwInfo()) {
|
||||||
GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
|
displayMessage(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
|
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void done() {
|
private void done() {
|
||||||
|
LOG.info("Operation done.");
|
||||||
updateCoordinator = null;
|
updateCoordinator = null;
|
||||||
operationFinished();
|
operationFinished();
|
||||||
unsetBusy();
|
unsetBusy();
|
||||||
@ -93,36 +96,39 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (updateCoordinator == null) {
|
if (updateCoordinator == null) {
|
||||||
LOG.error("received notification when updateCoordinator is null, ignoring!");
|
LOG.error("received notification when updateCoordinator is null, ignoring (notification content follows):");
|
||||||
|
getSupport().logMessageContent(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (value[0]) {
|
switch (value[0]) {
|
||||||
case MiBandService.NOTIFY_FW_CHECK_SUCCESS:
|
case MiBandService.NOTIFY_FW_CHECK_SUCCESS:
|
||||||
if (firmwareInfoSent) {
|
if (firmwareInfoSent) {
|
||||||
GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO);
|
displayMessage(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO);
|
||||||
if (!updateCoordinator.sendFwData()) {
|
if (!updateCoordinator.sendFwData()) {
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
firmwareInfoSent = false;
|
firmwareInfoSent = false;
|
||||||
|
} else {
|
||||||
|
LOG.warn("firmwareInfoSent is false -- not sending firmware data even though we got meta data success notification");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MiBandService.NOTIFY_FW_CHECK_FAILED:
|
case MiBandService.NOTIFY_FW_CHECK_FAILED:
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR);
|
displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
firmwareInfoSent = false;
|
firmwareInfoSent = false;
|
||||||
done();
|
done();
|
||||||
break;
|
break;
|
||||||
case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS:
|
case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS:
|
||||||
if (updateCoordinator.initNextOperation()) {
|
if (updateCoordinator.initNextOperation()) {
|
||||||
GB.toast(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO);
|
displayMessage(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO);
|
||||||
if (!updateCoordinator.sendFwInfo()) {
|
if (!updateCoordinator.sendFwInfo()) {
|
||||||
GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
|
displayMessage(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else if (updateCoordinator.needsReboot()) {
|
} else if (updateCoordinator.needsReboot()) {
|
||||||
GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO);
|
displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO);
|
||||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
||||||
getSupport().onReboot();
|
getSupport().onReboot();
|
||||||
} else {
|
} else {
|
||||||
@ -132,7 +138,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
break;
|
break;
|
||||||
case MiBandService.NOTIFY_FIRMWARE_UPDATE_FAILED:
|
case MiBandService.NOTIFY_FIRMWARE_UPDATE_FAILED:
|
||||||
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext());
|
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext());
|
||||||
done();
|
done();
|
||||||
break;
|
break;
|
||||||
@ -143,6 +149,10 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void displayMessage(Context context, String message, int duration, int severity) {
|
||||||
|
getSupport().handleGBDeviceEvent(new GBDeviceEventDisplayMessage(message, duration, severity));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the MiBand to receive the new firmware data.
|
* Prepare the MiBand to receive the new firmware data.
|
||||||
* Some information about the new firmware version have to be pushed to the MiBand before sending
|
* Some information about the new firmware version have to be pushed to the MiBand before sending
|
||||||
@ -193,7 +203,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
} else {
|
} else {
|
||||||
LOG.info("is multi Mi Band firmware, sending fw2 (hr) first");
|
LOG.info("is multi Mi Band firmware, sending fw2 (hr) first");
|
||||||
byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */);
|
byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */);
|
||||||
byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 1, rebootWhenFinished /*, progress monitor */);
|
byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 0, rebootWhenFinished /*, progress monitor */);
|
||||||
return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished);
|
return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,31 +278,34 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
final int packetLength = 20;
|
final int packetLength = 20;
|
||||||
int packets = len / packetLength;
|
int packets = len / packetLength;
|
||||||
|
|
||||||
|
BluetoothGattCharacteristic characteristicControlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
|
||||||
|
BluetoothGattCharacteristic characteristicFWData = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA);
|
||||||
|
try {
|
||||||
// going from 0 to len
|
// going from 0 to len
|
||||||
int firmwareProgress = 0;
|
int firmwareProgress = 0;
|
||||||
|
|
||||||
try {
|
|
||||||
TransactionBuilder builder = performInitialized("send firmware packet");
|
TransactionBuilder builder = performInitialized("send firmware packet");
|
||||||
|
// getSupport().setLowLatency(builder);
|
||||||
for (int i = 0; i < packets; i++) {
|
for (int i = 0; i < packets; i++) {
|
||||||
byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength);
|
byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength);
|
||||||
|
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk);
|
builder.write(characteristicFWData, fwChunk);
|
||||||
firmwareProgress += packetLength;
|
firmwareProgress += packetLength;
|
||||||
|
|
||||||
int progressPercent = (int) (((float) firmwareProgress) / len) * 100;
|
int progressPercent = (int) ((((float) firmwareProgress) / len) * 100);
|
||||||
if ((i > 0) && (i % 50 == 0)) {
|
if ((i > 0) && (i % 50 == 0)) {
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC});
|
builder.write(characteristicControlPoint, new byte[]{MiBandService.COMMAND_SYNC});
|
||||||
builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, progressPercent, getContext()));
|
builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, progressPercent, getContext()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firmwareProgress < len) {
|
if (firmwareProgress < len) {
|
||||||
byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len);
|
byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len);
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk);
|
builder.write(characteristicFWData, lastChunk);
|
||||||
firmwareProgress = len;
|
firmwareProgress = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC});
|
builder.write(characteristicControlPoint, new byte[]{MiBandService.COMMAND_SYNC});
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -319,9 +332,10 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
public boolean sendFwInfo() {
|
public boolean sendFwInfo() {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("send firmware info");
|
TransactionBuilder builder = performInitialized("send firmware info");
|
||||||
|
// getSupport().setLowLatency(builder);
|
||||||
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
|
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
|
||||||
|
builder.add(new FirmwareInfoSentAction()); // Note: *before* actually sending the info, otherwise it's too late!
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo());
|
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo());
|
||||||
builder.add(new FirmwareInfoSucceededAction());
|
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -437,10 +451,12 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FirmwareInfoSucceededAction extends PlainAction {
|
private class FirmwareInfoSentAction extends PlainAction {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(BluetoothGatt gatt) {
|
public boolean run(BluetoothGatt gatt) {
|
||||||
|
if (isOperationRunning()) {
|
||||||
firmwareInfoSent = true;
|
firmwareInfoSent = true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,22 +21,33 @@ class DatalogSessionHealthSleep extends DatalogSession {
|
|||||||
|
|
||||||
public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) {
|
public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) {
|
||||||
super(id, uuid, tag, item_type, item_size);
|
super(id, uuid, tag, item_type, item_size);
|
||||||
taginfo = "(health - sleep)";
|
taginfo = "(health - sleep " + tag + " )";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
|
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
|
||||||
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
|
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
|
||||||
|
switch (this.tag) {
|
||||||
|
case 83:
|
||||||
|
return handleMessage83(datalogMessage, length);
|
||||||
|
case 84:
|
||||||
|
return handleMessage84(datalogMessage, length);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleMessage84(ByteBuffer datalogMessage, int length) {
|
||||||
int initialPosition = datalogMessage.position();
|
int initialPosition = datalogMessage.position();
|
||||||
int beginOfRecordPosition;
|
int beginOfRecordPosition;
|
||||||
short recordVersion; //probably
|
short recordVersion; //probably
|
||||||
|
short recordType; //probably: 1=sleep, 2=deep sleep
|
||||||
|
|
||||||
if (0 != (length % itemSize))
|
if (0 != (length % itemSize))
|
||||||
return false;//malformed message?
|
return false;//malformed message?
|
||||||
|
|
||||||
int recordCount = length / itemSize;
|
int recordCount = length / itemSize;
|
||||||
SleepRecord[] sleepRecords = new SleepRecord[recordCount];
|
SleepRecord84[] sleepRecords = new SleepRecord84[recordCount];
|
||||||
|
|
||||||
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
|
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
|
||||||
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
|
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
|
||||||
@ -45,26 +56,30 @@ class DatalogSessionHealthSleep extends DatalogSession {
|
|||||||
if (recordVersion != 1)
|
if (recordVersion != 1)
|
||||||
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
|
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
|
||||||
|
|
||||||
sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(),
|
datalogMessage.getShort();//throwaway, unknown
|
||||||
datalogMessage.getInt(),
|
recordType = datalogMessage.getShort();
|
||||||
datalogMessage.getInt(),
|
|
||||||
datalogMessage.getInt());
|
sleepRecords[recordIdx] = new SleepRecord84(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
|
return store84(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean store(SleepRecord[] sleepRecords) {
|
private boolean store84(SleepRecord84[] sleepRecords) {
|
||||||
DBHandler dbHandler = null;
|
DBHandler dbHandler = null;
|
||||||
SampleProvider sampleProvider = new HealthSampleProvider();
|
SampleProvider sampleProvider = new HealthSampleProvider();
|
||||||
GB.toast("We don't know how to store deep sleep from the pebble yet.", Toast.LENGTH_LONG, GB.INFO);
|
|
||||||
try {
|
try {
|
||||||
dbHandler = GBApplication.acquireDB();
|
dbHandler = GBApplication.acquireDB();
|
||||||
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
|
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
|
||||||
for (SleepRecord sleepRecord : sleepRecords) {
|
for (SleepRecord84 sleepRecord : sleepRecords) {
|
||||||
if (latestTimestamp < sleepRecord.bedTimeEnd)
|
if (latestTimestamp < (sleepRecord.timestampStart + sleepRecord.durationSeconds))
|
||||||
return false;
|
return false;
|
||||||
dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
|
if (sleepRecord.type == 2) {
|
||||||
|
dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), sampleProvider);
|
||||||
|
} else {
|
||||||
|
dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.debug(ex.getMessage());
|
LOG.debug(ex.getMessage());
|
||||||
@ -76,17 +91,80 @@ class DatalogSessionHealthSleep extends DatalogSession {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SleepRecord {
|
private boolean handleMessage83(ByteBuffer datalogMessage, int length) {
|
||||||
|
int initialPosition = datalogMessage.position();
|
||||||
|
int beginOfRecordPosition;
|
||||||
|
short recordVersion; //probably
|
||||||
|
|
||||||
|
if (0 != (length % itemSize))
|
||||||
|
return false;//malformed message?
|
||||||
|
|
||||||
|
int recordCount = length / itemSize;
|
||||||
|
SleepRecord83[] sleepRecords = new SleepRecord83[recordCount];
|
||||||
|
|
||||||
|
for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) {
|
||||||
|
beginOfRecordPosition = initialPosition + recordIdx * itemSize;
|
||||||
|
datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record
|
||||||
|
recordVersion = datalogMessage.getShort();
|
||||||
|
if (recordVersion != 1)
|
||||||
|
return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
|
||||||
|
|
||||||
|
sleepRecords[recordIdx] = new SleepRecord83(datalogMessage.getInt(),
|
||||||
|
datalogMessage.getInt(),
|
||||||
|
datalogMessage.getInt(),
|
||||||
|
datalogMessage.getInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
return store83(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again.
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean store83(SleepRecord83[] sleepRecords) {
|
||||||
|
DBHandler dbHandler = null;
|
||||||
|
SampleProvider sampleProvider = new HealthSampleProvider();
|
||||||
|
GB.toast("Deep sleep is supported only from firmware 3.11 onwards.", Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
try {
|
||||||
|
dbHandler = GBApplication.acquireDB();
|
||||||
|
int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider);
|
||||||
|
for (SleepRecord83 sleepRecord : sleepRecords) {
|
||||||
|
if (latestTimestamp < sleepRecord.bedTimeEnd)
|
||||||
|
return false;
|
||||||
|
dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.debug(ex.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (dbHandler != null) {
|
||||||
|
dbHandler.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SleepRecord83 {
|
||||||
int offsetUTC; //probably
|
int offsetUTC; //probably
|
||||||
int bedTimeStart;
|
int bedTimeStart;
|
||||||
int bedTimeEnd;
|
int bedTimeEnd;
|
||||||
int deepSleepSeconds;
|
int deepSleepSeconds;
|
||||||
|
|
||||||
public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) {
|
public SleepRecord83(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) {
|
||||||
this.offsetUTC = offsetUTC;
|
this.offsetUTC = offsetUTC;
|
||||||
this.bedTimeStart = bedTimeStart;
|
this.bedTimeStart = bedTimeStart;
|
||||||
this.bedTimeEnd = bedTimeEnd;
|
this.bedTimeEnd = bedTimeEnd;
|
||||||
this.deepSleepSeconds = deepSleepSeconds;
|
this.deepSleepSeconds = deepSleepSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SleepRecord84 {
|
||||||
|
int type; //1=sleep, 2=deep sleep
|
||||||
|
int offsetUTC; //probably
|
||||||
|
int timestampStart;
|
||||||
|
int durationSeconds;
|
||||||
|
|
||||||
|
public SleepRecord84(int type, int offsetUTC, int timestampStart, int durationSeconds) {
|
||||||
|
this.type = type;
|
||||||
|
this.offsetUTC = offsetUTC;
|
||||||
|
this.timestampStart = timestampStart;
|
||||||
|
this.durationSeconds = durationSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -46,7 +46,7 @@ public class DatalogSessionHealthSteps extends DatalogSession {
|
|||||||
|
|
||||||
recordVersion = datalogMessage.getShort();
|
recordVersion = datalogMessage.getShort();
|
||||||
|
|
||||||
if (recordVersion != 5)
|
if ((recordVersion != 5) && (recordVersion != 6))
|
||||||
return false; //we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
|
return false; //we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it
|
||||||
|
|
||||||
timestamp = datalogMessage.getInt();
|
timestamp = datalogMessage.getInt();
|
||||||
@ -59,8 +59,7 @@ public class DatalogSessionHealthSteps extends DatalogSession {
|
|||||||
|
|
||||||
for (int recordIdx = 0; recordIdx < recordNum; recordIdx++) {
|
for (int recordIdx = 0; recordIdx < recordNum; recordIdx++) {
|
||||||
datalogMessage.position(beginOfRecordPosition + recordIdx * recordLength); //we may not consume all the bytes of a record
|
datalogMessage.position(beginOfRecordPosition + recordIdx * recordLength); //we may not consume all the bytes of a record
|
||||||
stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get() & 0xff, datalogMessage.get() & 0xff, datalogMessage.getShort() & 0xffff);
|
stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get() & 0xff, datalogMessage.get() & 0xff, datalogMessage.getShort() & 0xffff, datalogMessage.get() & 0xff);
|
||||||
datalogMessage.getShort(); // skip
|
|
||||||
timestamp += 60;
|
timestamp += 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,12 +101,14 @@ public class DatalogSessionHealthSteps extends DatalogSession {
|
|||||||
int steps;
|
int steps;
|
||||||
int orientation;
|
int orientation;
|
||||||
int intensity;
|
int intensity;
|
||||||
|
int light_intensity;
|
||||||
|
|
||||||
public StepsRecord(int timestamp, int steps, int orientation, int intensity) {
|
public StepsRecord(int timestamp, int steps, int orientation, int intensity, int light_intensity) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.steps = steps;
|
this.steps = steps;
|
||||||
this.orientation = orientation;
|
this.orientation = orientation;
|
||||||
this.intensity = intensity;
|
this.intensity = intensity;
|
||||||
|
this.light_intensity = light_intensity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,11 +200,16 @@ public class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false));
|
mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false));
|
||||||
gbDevice.setState(GBDevice.State.CONNECTED);
|
|
||||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
|
||||||
|
|
||||||
mIsConnected = true;
|
mIsConnected = true;
|
||||||
|
if (originalState == GBDevice.State.WAITING_FOR_RECONNECT) {
|
||||||
|
gbDevice.setState(GBDevice.State.INITIALIZED);
|
||||||
|
} else {
|
||||||
|
gbDevice.setState(GBDevice.State.CONNECTED);
|
||||||
write(mPebbleProtocol.encodeFirmwareVersionReq());
|
write(mPebbleProtocol.encodeFirmwareVersionReq());
|
||||||
|
}
|
||||||
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,9 +368,19 @@ public class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
if (reconnectAttempts > 0) {
|
if (reconnectAttempts > 0) {
|
||||||
gbDevice.setState(GBDevice.State.CONNECTING);
|
gbDevice.setState(GBDevice.State.CONNECTING);
|
||||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||||
|
int delaySeconds = 1;
|
||||||
while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) {
|
while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) {
|
||||||
LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")");
|
LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")");
|
||||||
mIsConnected = connect(gbDevice.getAddress());
|
mIsConnected = connect(gbDevice.getAddress());
|
||||||
|
if (!mIsConnected) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(delaySeconds * 1000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
if (delaySeconds < 64) {
|
||||||
|
delaySeconds *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!mIsConnected && !mQuit) {
|
if (!mIsConnected && !mQuit) {
|
||||||
|
@ -33,9 +33,9 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID;
|
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||||
|
|
||||||
public class PebbleProtocol extends GBDeviceProtocol {
|
public class PebbleProtocol extends GBDeviceProtocol {
|
||||||
@ -495,7 +495,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encodeFindDevice(boolean start) {
|
public byte[] encodeFindDevice(boolean start) {
|
||||||
return encodeSetCallState("Where are you?", "Gadgetbridge", start ? ServiceCommand.CALL_INCOMING : ServiceCommand.CALL_END);
|
return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) {
|
private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) {
|
||||||
@ -1044,20 +1044,20 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encodeSetCallState(String number, String name, ServiceCommand command) {
|
public byte[] encodeSetCallState(String number, String name, int command) {
|
||||||
String[] parts = {number, name};
|
String[] parts = {number, name};
|
||||||
byte pebbleCmd;
|
byte pebbleCmd;
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CALL_START:
|
case CallSpec.CALL_START:
|
||||||
pebbleCmd = PHONECONTROL_START;
|
pebbleCmd = PHONECONTROL_START;
|
||||||
break;
|
break;
|
||||||
case CALL_END:
|
case CallSpec.CALL_END:
|
||||||
pebbleCmd = PHONECONTROL_END;
|
pebbleCmd = PHONECONTROL_END;
|
||||||
break;
|
break;
|
||||||
case CALL_INCOMING:
|
case CallSpec.CALL_INCOMING:
|
||||||
pebbleCmd = PHONECONTROL_INCOMINGCALL;
|
pebbleCmd = PHONECONTROL_INCOMINGCALL;
|
||||||
break;
|
break;
|
||||||
case CALL_OUTGOING:
|
case CallSpec.CALL_OUTGOING:
|
||||||
// pebbleCmd = PHONECONTROL_OUTGOINGCALL;
|
// pebbleCmd = PHONECONTROL_OUTGOINGCALL;
|
||||||
/*
|
/*
|
||||||
* HACK/WORKAROUND for non-working outgoing call display.
|
* HACK/WORKAROUND for non-working outgoing call display.
|
||||||
@ -1876,12 +1876,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
int timestamp = buf.getInt();
|
int timestamp = buf.getInt();
|
||||||
int log_tag = buf.getInt();
|
int log_tag = buf.getInt();
|
||||||
byte item_type = buf.get();
|
byte item_type = buf.get();
|
||||||
short item_size = buf.get();
|
short item_size = buf.getShort();
|
||||||
LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size);
|
LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size);
|
||||||
if (!mDatalogSessions.containsKey(id)) {
|
if (!mDatalogSessions.containsKey(id)) {
|
||||||
if (uuid.equals(UUID_ZERO) && log_tag == 81) {
|
if (uuid.equals(UUID_ZERO) && log_tag == 81) {
|
||||||
mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size));
|
mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size));
|
||||||
} else if (uuid.equals(UUID_ZERO) && log_tag == 83) {
|
} else if (uuid.equals(UUID_ZERO) && (log_tag == 83 || log_tag == 84)) {
|
||||||
mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size));
|
mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size));
|
||||||
} else {
|
} else {
|
||||||
mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size));
|
mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size));
|
||||||
|
@ -10,7 +10,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||||
@ -35,7 +39,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useAutoConnect() {
|
public boolean useAutoConnect() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,6 +75,45 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
|
|||||||
return (PebbleIoThread) super.getDeviceIOThread();
|
return (PebbleIoThread) super.getDeviceIOThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean reconnect() {
|
||||||
|
if (!isConnected() && useAutoConnect()) {
|
||||||
|
if (getDevice().getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
|
||||||
|
gbDeviceIOThread.interrupt();
|
||||||
|
gbDeviceIOThread = null;
|
||||||
|
if (!connect()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(4000); // this is about the time the connect takes, so the notification can come though
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNotification(NotificationSpec notificationSpec) {
|
||||||
|
if (reconnect()) {
|
||||||
|
super.onNotification(notificationSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
|
if (reconnect()) {
|
||||||
|
super.onSetCallState(callSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
|
if (reconnect()) {
|
||||||
|
super.onSetMusicInfo(musicSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||||
//nothing to do ATM
|
//nothing to do ATM
|
||||||
|
@ -8,8 +8,9 @@ import java.util.UUID;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,8 +30,8 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceSupport.class);
|
||||||
|
|
||||||
private GBDeviceProtocol gbDeviceProtocol;
|
protected GBDeviceProtocol gbDeviceProtocol;
|
||||||
private GBDeviceIoThread gbDeviceIOThread;
|
protected GBDeviceIoThread gbDeviceIOThread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to create the device specific GBDeviceProtocol instance to be used.
|
* Factory method to create the device specific GBDeviceProtocol instance to be used.
|
||||||
@ -47,11 +48,7 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
|||||||
// currently only one thread allowed
|
// currently only one thread allowed
|
||||||
if (gbDeviceIOThread != null) {
|
if (gbDeviceIOThread != null) {
|
||||||
gbDeviceIOThread.quit();
|
gbDeviceIOThread.quit();
|
||||||
try {
|
gbDeviceIOThread.interrupt();
|
||||||
gbDeviceIOThread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
gbDeviceIOThread = null;
|
gbDeviceIOThread = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,14 +117,14 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(String number, String name, ServiceCommand command) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
byte[] bytes = gbDeviceProtocol.encodeSetCallState(number, name, command);
|
byte[] bytes = gbDeviceProtocol.encodeSetCallState(callSpec.number, callSpec.name, callSpec.command);
|
||||||
sendToDevice(bytes);
|
sendToDevice(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(artist, album, track, duration, trackCount, trackNr);
|
byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(musicSpec.artist, musicSpec.album, musicSpec.track, musicSpec.duration, musicSpec.trackCount, musicSpec.trackNr);
|
||||||
sendToDevice(bytes);
|
sendToDevice(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,4 +175,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
|
|||||||
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeSteps(enable);
|
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeSteps(enable);
|
||||||
sendToDevice(bytes);
|
sendToDevice(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable);
|
||||||
|
sendToDevice(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
|
|
||||||
public abstract class GBDeviceProtocol {
|
public abstract class GBDeviceProtocol {
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ public abstract class GBDeviceProtocol {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] encodeSetCallState(String number, String name, ServiceCommand command) {
|
public byte[] encodeSetCallState(String number, String name, int command) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +59,10 @@ public abstract class GBDeviceProtocol {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] encodeEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ public class GB {
|
|||||||
public static final int INFO = 1;
|
public static final int INFO = 1;
|
||||||
public static final int WARN = 2;
|
public static final int WARN = 2;
|
||||||
public static final int ERROR = 3;
|
public static final int ERROR = 3;
|
||||||
|
public static final String ACTION_DISPLAY_MESSAGE = "GB_Display_Message";
|
||||||
|
public static final String DISPLAY_MESSAGE_MESSAGE = "message";
|
||||||
|
public static final String DISPLAY_MESSAGE_DURATION = "duration";
|
||||||
|
public static final String DISPLAY_MESSAGE_SEVERITY = "severity";
|
||||||
public static GBEnvironment environment;
|
public static GBEnvironment environment;
|
||||||
|
|
||||||
public static Notification createNotification(String text, Context context) {
|
public static Notification createNotification(String text, Context context) {
|
||||||
@ -225,7 +229,7 @@ public class GB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void log(String message, int severity, Throwable ex) {
|
public static void log(String message, int severity, Throwable ex) {
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case INFO:
|
case INFO:
|
||||||
LOG.info(message, ex);
|
LOG.info(message, ex);
|
||||||
|
@ -62,6 +62,14 @@
|
|||||||
android:layout_below="@+id/installProgressBar"
|
android:layout_below="@+id/installProgressBar"
|
||||||
android:layout_marginTop="10dp" />
|
android:layout_marginTop="10dp" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/detailsListView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/installButton"
|
||||||
|
android:layout_alignParentEnd="false">
|
||||||
|
</ListView>
|
||||||
|
|
||||||
<android.widget.Space
|
<android.widget.Space
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
42
app/src/main/res/layout/item_with_details_small.xml
Normal file
42
app/src/main/res/layout/item_with_details_small.xml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/activatedBackgroundIndicator"
|
||||||
|
android:padding="4dp" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/item_image"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:contentDescription="@string/candidate_item_device_image" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toEndOf="@+id/item_image"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollHorizontally="false"
|
||||||
|
style="@style/Base.TextAppearance.AppCompat.Body1"
|
||||||
|
android:text="Item Name"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Base.TextAppearance.AppCompat.Body2"
|
||||||
|
android:text="Item Description"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -102,7 +102,7 @@
|
|||||||
<string name="miband_pairing_using_dummy_userdata">有効なユーザーデータはありません。今のところ、ダミーのユーザーデータを使用します。</string>
|
<string name="miband_pairing_using_dummy_userdata">有効なユーザーデータはありません。今のところ、ダミーのユーザーデータを使用します。</string>
|
||||||
<string name="miband_pairing_tap_hint">お使いのMi Bandが振動と点滅したとき、それを連続して数回タップしてください。</string>
|
<string name="miband_pairing_tap_hint">お使いのMi Bandが振動と点滅したとき、それを連続して数回タップしてください。</string>
|
||||||
<string name="appinstaller_install">インストール</string>
|
<string name="appinstaller_install">インストール</string>
|
||||||
<string name="discovery_connected_devices_hint">お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。</string>
|
<string name="discovery_connected_devices_hint">お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。お使いのデバイスが 2 分しても表示されない場合は、モバイルデバイスを再起動した後にもう一度試してください。</string>
|
||||||
<string name="discovery_note">注:</string>
|
<string name="discovery_note">注:</string>
|
||||||
<string name="candidate_item_device_image">デバイスイメージ</string>
|
<string name="candidate_item_device_image">デバイスイメージ</string>
|
||||||
<string name="miband_prefs_alias">名前/別名</string>
|
<string name="miband_prefs_alias">名前/別名</string>
|
||||||
@ -197,6 +197,7 @@
|
|||||||
<string name="miband_fwinstaller_incompatible_version">非互換性のファームウェア</string>
|
<string name="miband_fwinstaller_incompatible_version">非互換性のファームウェア</string>
|
||||||
<string name="fwinstaller_firmware_not_compatible_to_device">このファームウェアは、デバイスと互換性がありません</string>
|
<string name="fwinstaller_firmware_not_compatible_to_device">このファームウェアは、デバイスと互換性がありません</string>
|
||||||
<string name="miband_prefs_reserve_alarm_calendar">今後のイベントのために予約するアラーム</string>
|
<string name="miband_prefs_reserve_alarm_calendar">今後のイベントのために予約するアラーム</string>
|
||||||
|
<string name="miband_prefs_hr_sleep_detection">睡眠の検出を改善するために心拍センサーを使用する</string>
|
||||||
<string name="waiting_for_reconnect">再接続の待機中</string>
|
<string name="waiting_for_reconnect">再接続の待機中</string>
|
||||||
<string name="appmananger_app_reinstall">再インストール</string>
|
<string name="appmananger_app_reinstall">再インストール</string>
|
||||||
<string name="activity_prefs_about_you">あなたについて</string>
|
<string name="activity_prefs_about_you">あなたについて</string>
|
||||||
@ -217,4 +218,6 @@
|
|||||||
<string name="device_fw">FW: %1$s</string>
|
<string name="device_fw">FW: %1$s</string>
|
||||||
<string name="error_creating_directory_for_logfiles">ログファイルのディレクトリを作成中にエラー: %1$s</string>
|
<string name="error_creating_directory_for_logfiles">ログファイルのディレクトリを作成中にエラー: %1$s</string>
|
||||||
<string name="DEVINFO_HR_VER">HR: </string>
|
<string name="DEVINFO_HR_VER">HR: </string>
|
||||||
|
<string name="updatefirmwareoperation_update_in_progress">ファームウェアを更新しています</string>
|
||||||
|
<string name="updatefirmwareoperation_firmware_not_sent">ファームウェアを送信しませんでした</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<color name="secondarytext" type="color">#ff808080</color>
|
<color name="secondarytext" type="color">#ff808080</color>
|
||||||
<color name="divider" type="color">#1f000000</color>
|
<color name="divider" type="color">#1f000000</color>
|
||||||
|
|
||||||
<color name="chart_heartrate" type="color">#b40000</color>
|
<color name="chart_heartrate" type="color">#ffab40</color>
|
||||||
<color name="chart_deep_sleep_light" type="color">#0071b7</color>
|
<color name="chart_deep_sleep_light" type="color">#0071b7</color>
|
||||||
<color name="chart_deep_sleep_dark" type="color">#4c5aff</color>
|
<color name="chart_deep_sleep_dark" type="color">#4c5aff</color>
|
||||||
|
|
||||||
|
@ -213,6 +213,8 @@
|
|||||||
<string name="miband_fwinstaller_incompatible_version">Incompatible firmware</string>
|
<string name="miband_fwinstaller_incompatible_version">Incompatible firmware</string>
|
||||||
<string name="fwinstaller_firmware_not_compatible_to_device">This firmware is not compatible with the device</string>
|
<string name="fwinstaller_firmware_not_compatible_to_device">This firmware is not compatible with the device</string>
|
||||||
<string name="miband_prefs_reserve_alarm_calendar">Alarms to reserve for upcoming events</string>
|
<string name="miband_prefs_reserve_alarm_calendar">Alarms to reserve for upcoming events</string>
|
||||||
|
<string name="miband_prefs_hr_sleep_detection">Use Heartrate Sensor to improve sleep detection</string>
|
||||||
|
|
||||||
<string name="waiting_for_reconnect">waiting for reconnect</string>
|
<string name="waiting_for_reconnect">waiting for reconnect</string>
|
||||||
<string name="appmananger_app_reinstall">Reinstall</string>
|
<string name="appmananger_app_reinstall">Reinstall</string>
|
||||||
|
|
||||||
@ -237,5 +239,6 @@
|
|||||||
<string name="DEVINFO_HR_VER">"HR: "</string>
|
<string name="DEVINFO_HR_VER">"HR: "</string>
|
||||||
<string name="updatefirmwareoperation_update_in_progress">Firmware update in progress</string>
|
<string name="updatefirmwareoperation_update_in_progress">Firmware update in progress</string>
|
||||||
<string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string>
|
<string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string>
|
||||||
|
<string name="charts_legend_heartrate">Heart Rate</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<changelog>
|
<changelog>
|
||||||
|
<release version="0.9.4" versioncode="48">
|
||||||
|
<change>Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!)</change>
|
||||||
|
<change>Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly</change>
|
||||||
|
<change>Pebble: delay between reconnection attempts (from 1 up to 64 seconds)</change>
|
||||||
|
</release>
|
||||||
|
<release version="0.9.3" versioncode="47">
|
||||||
|
<change>Pebble: Fix Pebble Health activation (was not available in the App Manager)</change>
|
||||||
|
<change>Simplify connection state display (only connecting->connected)</change>
|
||||||
|
<change>Small improvements to the pairing activity</change>
|
||||||
|
<change>Mi Band 1S: Fix for mi band firmware update</change>
|
||||||
|
</release>
|
||||||
<release version="0.9.2" versioncode="46">
|
<release version="0.9.2" versioncode="46">
|
||||||
<change>Mi Band: Fix update of second (HR) firmware on Mi1S (#234)</change>
|
<change>Mi Band: Fix update of second (HR) firmware on Mi1S (#234)</change>
|
||||||
<change>Fix ordering issue of device infos being displayed</change>
|
<change>Fix ordering issue of device infos being displayed</change>
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
android:maxLength="1"
|
android:maxLength="1"
|
||||||
android:digits="0123"
|
android:digits="0123"
|
||||||
android:title="@string/miband_prefs_reserve_alarm_calendar" />
|
android:title="@string/miband_prefs_reserve_alarm_calendar" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="mi_hr_sleep_detection"
|
||||||
|
android:title="@string/miband_prefs_hr_sleep_detection" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
@ -3,15 +3,15 @@ package nodomain.freeyourgadget.gadgetbridge.service;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
|
|
||||||
|
|
||||||
public class TestDeviceSupport extends AbstractDeviceSupport {
|
public class TestDeviceSupport extends AbstractDeviceSupport {
|
||||||
|
|
||||||
@ -61,12 +61,12 @@ public class TestDeviceSupport extends AbstractDeviceSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command) {
|
public void onSetCallState(CallSpec callSpec) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,4 +124,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport {
|
|||||||
public void onEnableRealtimeSteps(boolean enable) {
|
public void onEnableRealtimeSteps(boolean enable) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
Loading…
Reference in New Issue
Block a user