mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 10:56:50 +01:00
Added PineTime (InfiniTime) Nordic DFU support
This commit is contained in:
parent
2b889a2e4c
commit
3b5f35f34a
@ -1,5 +1,5 @@
|
|||||||
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
Gobbetti, Lem Dulfo
|
Gobbetti, Lem Dulfo, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -66,6 +66,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
|||||||
private boolean mayConnect;
|
private boolean mayConnect;
|
||||||
|
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
private TextView progressText;
|
||||||
private ListView itemListView;
|
private ListView itemListView;
|
||||||
private final List<ItemWithDetails> items = new ArrayList<>();
|
private final List<ItemWithDetails> items = new ArrayList<>();
|
||||||
private ItemWithDetailsAdapter itemAdapter;
|
private ItemWithDetailsAdapter itemAdapter;
|
||||||
@ -94,6 +95,23 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
|||||||
validateInstallation();
|
validateInstallation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (GB.ACTION_SET_PROGRESS_BAR.equals(action)) {
|
||||||
|
if (intent.hasExtra(GB.PROGRESS_BAR_INDETERMINATE)) {
|
||||||
|
setProgressIndeterminate(intent.getBooleanExtra(GB.PROGRESS_BAR_INDETERMINATE, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent.hasExtra(GB.PROGRESS_BAR_PROGRESS)) {
|
||||||
|
setProgressIndeterminate(false);
|
||||||
|
setProgressBar(intent.getIntExtra(GB.PROGRESS_BAR_PROGRESS, 0));
|
||||||
|
}
|
||||||
|
} else if (GB.ACTION_SET_PROGRESS_TEXT.equals(action)) {
|
||||||
|
if (intent.hasExtra(GB.DISPLAY_MESSAGE_MESSAGE)) {
|
||||||
|
setProgressText(intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE));
|
||||||
|
}
|
||||||
|
} else if (GB.ACTION_SET_INFO_TEXT.equals(action)) {
|
||||||
|
if (intent.hasExtra(GB.DISPLAY_MESSAGE_MESSAGE)) {
|
||||||
|
setInfoText(intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE));
|
||||||
|
}
|
||||||
} else if (GB.ACTION_DISPLAY_MESSAGE.equals(action)) {
|
} else if (GB.ACTION_DISPLAY_MESSAGE.equals(action)) {
|
||||||
String message = intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE);
|
String message = intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE);
|
||||||
int severity = intent.getIntExtra(GB.DISPLAY_MESSAGE_SEVERITY, GB.INFO);
|
int severity = intent.getIntExtra(GB.DISPLAY_MESSAGE_SEVERITY, GB.INFO);
|
||||||
@ -114,6 +132,20 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProgressIndeterminate(boolean indeterminate) {
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
progressBar.setIndeterminate(indeterminate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressBar(int progress) {
|
||||||
|
progressBar.setProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressText(String text) {
|
||||||
|
progressText.setVisibility(View.VISIBLE);
|
||||||
|
progressText.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
private void connect() {
|
private void connect() {
|
||||||
mayConnect = false; // only do that once per #onCreate
|
mayConnect = false; // only do that once per #onCreate
|
||||||
GBApplication.deviceService().connect(device);
|
GBApplication.deviceService().connect(device);
|
||||||
@ -148,6 +180,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
|||||||
fwAppInstallTextView = findViewById(R.id.infoTextView);
|
fwAppInstallTextView = findViewById(R.id.infoTextView);
|
||||||
installButton = findViewById(R.id.installButton);
|
installButton = findViewById(R.id.installButton);
|
||||||
progressBar = findViewById(R.id.installProgressBar);
|
progressBar = findViewById(R.id.installProgressBar);
|
||||||
|
progressText = findViewById(R.id.installProgressText);
|
||||||
detailsListView = findViewById(R.id.detailsListView);
|
detailsListView = findViewById(R.id.detailsListView);
|
||||||
detailsAdapter = new ItemWithDetailsAdapter(this, details);
|
detailsAdapter = new ItemWithDetailsAdapter(this, details);
|
||||||
detailsAdapter.setSize(ItemWithDetailsAdapter.SIZE_SMALL);
|
detailsAdapter.setSize(ItemWithDetailsAdapter.SIZE_SMALL);
|
||||||
@ -157,6 +190,9 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
|||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
filter.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
||||||
filter.addAction(GB.ACTION_DISPLAY_MESSAGE);
|
filter.addAction(GB.ACTION_DISPLAY_MESSAGE);
|
||||||
|
filter.addAction(GB.ACTION_SET_PROGRESS_BAR);
|
||||||
|
filter.addAction(GB.ACTION_SET_PROGRESS_TEXT);
|
||||||
|
filter.addAction(GB.ACTION_SET_INFO_TEXT);
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
|
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
|
||||||
|
|
||||||
installButton.setOnClickListener(new View.OnClickListener() {
|
installButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/* Copyright (C) 2020 Taavi Eomäe
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.pinetime;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import no.nordicsemi.android.dfu.DfuBaseService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||||
|
|
||||||
|
public class PineTimeDFUService extends DfuBaseService {
|
||||||
|
@Override
|
||||||
|
protected Class<? extends Activity> getNotificationTarget() {
|
||||||
|
return FwAppInstallerActivity.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDebug() {
|
||||||
|
return BuildConfig.DEBUG;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/* Copyright (C) 2020 Taavi Eomäe
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.pinetime;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||||
|
|
||||||
|
public class PineTimeInstallHandler implements InstallHandler {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PineTimeInstallHandler.class);
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private boolean valid = false;
|
||||||
|
private String version = "(Unknown version)";
|
||||||
|
|
||||||
|
public PineTimeInstallHandler(Uri uri, Context context) {
|
||||||
|
this.context = context;
|
||||||
|
UriHelper uriHelper;
|
||||||
|
try {
|
||||||
|
uriHelper = UriHelper.get(uri, this.context);
|
||||||
|
} catch (IOException e) {
|
||||||
|
valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) {
|
||||||
|
byte[] bytes = new byte[32];
|
||||||
|
int read = in.read(bytes);
|
||||||
|
if (read < 32) {
|
||||||
|
valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateInstallation(InstallActivity installActivity, GBDevice device) {
|
||||||
|
if (device.isBusy()) {
|
||||||
|
installActivity.setInfoText(device.getBusyTask());
|
||||||
|
installActivity.setInstallEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device.getType() != DeviceType.PINETIME_JF || !device.isConnected()) {
|
||||||
|
installActivity.setInfoText("Firmware cannot be installed");
|
||||||
|
installActivity.setInstallEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericItem installItem = new GenericItem();
|
||||||
|
installItem.setIcon(R.drawable.ic_firmware);
|
||||||
|
installItem.setName("PineTime firmware");
|
||||||
|
installItem.setDetails(version);
|
||||||
|
|
||||||
|
installActivity.setInfoText(context.getString(R.string.firmware_install_warning, "(unknown)"));
|
||||||
|
installActivity.setInstallEnabled(true);
|
||||||
|
installActivity.setInstallItem(installItem);
|
||||||
|
LOG.debug("Initialized PineTimeInstallHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartInstall(GBDevice device) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Andreas Shimokawa
|
/* Copyright (C) 2020 Andreas Shimokawa, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -55,7 +55,8 @@ public class PineTimeJFCoordinator extends AbstractDeviceCoordinator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||||
return null;
|
PineTimeInstallHandler handler = new PineTimeInstallHandler(uri, context);
|
||||||
|
return handler.isValid() ? handler : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,11 +20,13 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import java.text.Collator;
|
import java.text.Collator;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class GenericItem implements ItemWithDetails {
|
public class GenericItem implements ItemWithDetails {
|
||||||
private String name;
|
private String name;
|
||||||
private String details;
|
private String details;
|
||||||
private int icon;
|
private int icon;
|
||||||
|
private boolean warning = false;
|
||||||
|
|
||||||
public static final Parcelable.Creator CREATOR = new Parcelable.Creator<GenericItem>() {
|
public static final Parcelable.Creator CREATOR = new Parcelable.Creator<GenericItem>() {
|
||||||
@Override
|
@Override
|
||||||
@ -56,9 +58,9 @@ public class GenericItem implements ItemWithDetails {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(getName());
|
dest.writeString(name);
|
||||||
dest.writeString(getDetails());
|
dest.writeString(details);
|
||||||
dest.writeInt(getIcon());
|
dest.writeInt(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
@ -66,6 +68,9 @@ public class GenericItem implements ItemWithDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setDetails(String details) {
|
public void setDetails(String details) {
|
||||||
|
if (details.equals("(Unknown version)")) {
|
||||||
|
this.warning = true;
|
||||||
|
}
|
||||||
this.details = details;
|
this.details = details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +78,14 @@ public class GenericItem implements ItemWithDetails {
|
|||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getWarning() {
|
||||||
|
return this.warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWarning(boolean enable) {
|
||||||
|
this.warning = enable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@ -95,32 +108,36 @@ public class GenericItem implements ItemWithDetails {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (o == null || getClass() != o.getClass()) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
GenericItem that = (GenericItem) o;
|
GenericItem that = (GenericItem) o;
|
||||||
|
|
||||||
return !(getName() != null ? !getName().equals(that.getName()) : that.getName() != null);
|
return Objects.equals(name, that.name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getName() != null ? getName().hashCode() : 0;
|
return name != null ? name.hashCode() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(ItemWithDetails another) {
|
public int compareTo(ItemWithDetails another) {
|
||||||
if (getName().equals(another.getName())) {
|
if (name.equals(another.getName())) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getName() == null) {
|
if (name == null) {
|
||||||
return +1;
|
return +1;
|
||||||
} else if (another.getName() == null) {
|
} else if (another.getName() == null) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collator.getInstance().compare(getName(), another.getName());
|
return Collator.getInstance().compare(name, another.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ import android.bluetooth.BluetoothGatt;
|
|||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -27,11 +30,19 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import no.nordicsemi.android.dfu.DfuLogListener;
|
||||||
|
import no.nordicsemi.android.dfu.DfuProgressListener;
|
||||||
|
import no.nordicsemi.android.dfu.DfuProgressListenerAdapter;
|
||||||
|
import no.nordicsemi.android.dfu.DfuServiceController;
|
||||||
|
import no.nordicsemi.android.dfu.DfuServiceInitiator;
|
||||||
|
import no.nordicsemi.android.dfu.DfuServiceListenerHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFConstants;
|
import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeDFUService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeInstallHandler;
|
||||||
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.CalendarEventSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
@ -52,13 +63,12 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.OverflowStrategy;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.OverflowStrategy;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuLogListener {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PineTimeJFSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PineTimeJFSupport.class);
|
||||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
|
||||||
private final DeviceInfoProfile<PineTimeJFSupport> deviceInfoProfile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are used to keep track when long strings haven't changed,
|
* These are used to keep track when long strings haven't changed,
|
||||||
@ -70,6 +80,126 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
String lastTrack;
|
String lastTrack;
|
||||||
String lastArtist;
|
String lastArtist;
|
||||||
|
|
||||||
|
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||||
|
|
||||||
|
private final DeviceInfoProfile<PineTimeJFSupport> deviceInfoProfile;
|
||||||
|
|
||||||
|
PineTimeInstallHandler handler;
|
||||||
|
DfuServiceController controller;
|
||||||
|
|
||||||
|
private final DfuProgressListener progressListener = new DfuProgressListenerAdapter() {
|
||||||
|
private final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getContext());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the progress bar to indeterminate or not, also makes it visible
|
||||||
|
*
|
||||||
|
* @param indeterminate if indeterminate
|
||||||
|
*/
|
||||||
|
public void setIndeterminate(boolean indeterminate) {
|
||||||
|
manager.sendBroadcast(new Intent(GB.ACTION_SET_PROGRESS_BAR).putExtra(GB.PROGRESS_BAR_INDETERMINATE, indeterminate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the status text and logs it
|
||||||
|
*/
|
||||||
|
public void setProgress(int progress) {
|
||||||
|
manager.sendBroadcast(new Intent(GB.ACTION_SET_PROGRESS_BAR).putExtra(GB.PROGRESS_BAR_PROGRESS, progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text that describes progress
|
||||||
|
*
|
||||||
|
* @param progressText text to display
|
||||||
|
*/
|
||||||
|
public void setProgressText(String progressText) {
|
||||||
|
manager.sendBroadcast(new Intent(GB.ACTION_SET_PROGRESS_TEXT).putExtra(GB.DISPLAY_MESSAGE_MESSAGE, progressText));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceConnecting(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Device is connecting");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceConnected(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Device is connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnablingDfuMode(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Upload is starting");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDfuProcessStarting(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Upload is starting");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDfuProcessStarted(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Upload has started");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDisconnecting(final String mac) {
|
||||||
|
this.setProgressText("Device is disconnecting!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDisconnected(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Device has disconnected!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDfuCompleted(final String mac) {
|
||||||
|
this.setProgressText("Upload has completed");
|
||||||
|
this.setIndeterminate(false);
|
||||||
|
this.setProgress(100);
|
||||||
|
|
||||||
|
handler = null;
|
||||||
|
controller = null;
|
||||||
|
DfuServiceListenerHelper.unregisterProgressListener(getContext(), progressListener);
|
||||||
|
|
||||||
|
// TODO: Request reconnection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFirmwareValidating(final String mac) {
|
||||||
|
this.setIndeterminate(true);
|
||||||
|
this.setProgressText("Upload is being validated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDfuAborted(final String mac) {
|
||||||
|
this.setProgressText("Upload has been aborted!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final String mac, int error, int errorType, final String message) {
|
||||||
|
this.setProgressText("Upload has failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(final String mac,
|
||||||
|
int percent,
|
||||||
|
float speed,
|
||||||
|
float averageSpeed,
|
||||||
|
int segment,
|
||||||
|
int totalSegments) {
|
||||||
|
this.setProgress(percent);
|
||||||
|
this.setIndeterminate(false);
|
||||||
|
this.setProgressText(String.format(Locale.ENGLISH,
|
||||||
|
"Upload is in progress\n%1s%% at %.2fkbps (average %.2fkbps)\nPart %1d of %1d",
|
||||||
|
percent, speed, averageSpeed, segment, totalSegments));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public PineTimeJFSupport() {
|
public PineTimeJFSupport() {
|
||||||
super(LOG);
|
super(LOG);
|
||||||
addSupportedService(GattService.UUID_SERVICE_ALERT_NOTIFICATION);
|
addSupportedService(GattService.UUID_SERVICE_ALERT_NOTIFICATION);
|
||||||
@ -92,6 +222,32 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
addSupportedProfile(deviceInfoProfile);
|
addSupportedProfile(deviceInfoProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
|
requestDeviceInfo(builder);
|
||||||
|
onSetTime();
|
||||||
|
builder.notify(getCharacteristic(UUID_CHARACTERISTICS_MUSIC_EVENT), true);
|
||||||
|
setInitialized(builder);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInitialized(TransactionBuilder builder) {
|
||||||
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestDeviceInfo(TransactionBuilder builder) {
|
||||||
|
LOG.debug("Requesting Device Info!");
|
||||||
|
deviceInfoProfile.requestDeviceInfo(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDeviceInfo(DeviceInfo info) {
|
||||||
|
LOG.warn("Device info: " + info);
|
||||||
|
versionCmd.hwVersion = info.getHardwareRevision();
|
||||||
|
versionCmd.fwVersion = info.getFirmwareRevision();
|
||||||
|
handleGBDeviceEvent(versionCmd);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useAutoConnect() {
|
public boolean useAutoConnect() {
|
||||||
return false;
|
return false;
|
||||||
@ -113,7 +269,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetTime() {
|
public void onSetTime() {
|
||||||
// since this is a standard we should generalize this in Gadgetbridge (properly)
|
// Since this is a standard we should generalize this in Gadgetbridge (properly)
|
||||||
GregorianCalendar now = BLETypeConversions.createCalendar();
|
GregorianCalendar now = BLETypeConversions.createCalendar();
|
||||||
byte[] bytes = BLETypeConversions.calendarToRawBytes(now);
|
byte[] bytes = BLETypeConversions.calendarToRawBytes(now);
|
||||||
byte[] tail = new byte[]{0, BLETypeConversions.mapTimeZone(now.getTimeZone(), BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ)};
|
byte[] tail = new byte[]{0, BLETypeConversions.mapTimeZone(now.getTimeZone(), BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ)};
|
||||||
@ -146,7 +302,35 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInstallApp(Uri uri) {
|
public void onInstallApp(Uri uri) {
|
||||||
|
try {
|
||||||
|
handler = new PineTimeInstallHandler(uri, getContext());
|
||||||
|
|
||||||
|
// TODO: Check validity more closely
|
||||||
|
if (true) {
|
||||||
|
DfuServiceInitiator starter = new DfuServiceInitiator(getDevice().getAddress())
|
||||||
|
.setDeviceName(getDevice().getName())
|
||||||
|
.setKeepBond(true)
|
||||||
|
.setForeground(false)
|
||||||
|
.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(false)
|
||||||
|
.setMtu(517)
|
||||||
|
.setZip(uri);
|
||||||
|
|
||||||
|
controller = starter.start(getContext(), PineTimeDFUService.class);
|
||||||
|
DfuServiceListenerHelper.registerProgressListener(getContext(), progressListener);
|
||||||
|
DfuServiceListenerHelper.registerLogListener(getContext(), this);
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent(GB.ACTION_SET_PROGRESS_BAR)
|
||||||
|
.putExtra(GB.PROGRESS_BAR_INDETERMINATE, true)
|
||||||
|
);
|
||||||
|
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent(GB.ACTION_SET_PROGRESS_TEXT)
|
||||||
|
.putExtra(GB.DISPLAY_MESSAGE_MESSAGE, "Starting DFU")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// TODO: Handle invalid firmware files
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
GB.toast(getContext(), "Firmware cannot be installed: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,11 +380,14 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFindDevice(boolean start) {
|
public void onFindDevice(boolean start) {
|
||||||
onSetConstantVibration(start ? 0xff : 0x00);
|
TransactionBuilder builder = new TransactionBuilder("Enable alert");
|
||||||
|
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL), new byte[]{(byte) (start ? 0x01 : 0x00)});
|
||||||
|
builder.queue(getQueue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetConstantVibration(int intensity) {
|
public void onSetConstantVibration(int intensity) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -268,7 +455,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("error sending music info", e);
|
LOG.error("Error sending music info", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +491,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("error sending music state", e);
|
LOG.error("Error sending music state", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -417,4 +604,12 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport {
|
|||||||
versionCmd.fwVersion = info.getFirmwareRevision();
|
versionCmd.fwVersion = info.getFirmwareRevision();
|
||||||
handleGBDeviceEvent(versionCmd);
|
handleGBDeviceEvent(versionCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nordic DFU needs this function to log DFU-related messages
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onLogEvent(final String deviceAddress, final int level, final String message) {
|
||||||
|
LOG.debug(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,14 @@ public class GB {
|
|||||||
public static final String DISPLAY_MESSAGE_DURATION = "duration";
|
public static final String DISPLAY_MESSAGE_DURATION = "duration";
|
||||||
public static final String DISPLAY_MESSAGE_SEVERITY = "severity";
|
public static final String DISPLAY_MESSAGE_SEVERITY = "severity";
|
||||||
|
|
||||||
|
/** Commands related to the progress (bar) on the screen */
|
||||||
|
public static final String ACTION_SET_PROGRESS_BAR = "GB_Set_Progress_Bar";
|
||||||
|
public static final String PROGRESS_BAR_INDETERMINATE = "indeterminate";
|
||||||
|
public static final String PROGRESS_BAR_MAX = "max";
|
||||||
|
public static final String PROGRESS_BAR_PROGRESS = "progress";
|
||||||
|
public static final String ACTION_SET_PROGRESS_TEXT = "GB_Set_Progress_Text";
|
||||||
|
public static final String ACTION_SET_INFO_TEXT = "GB_Set_Info_Text";
|
||||||
|
|
||||||
private static PendingIntent getContentIntent(Context context) {
|
private static PendingIntent getContentIntent(Context context) {
|
||||||
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
|
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
|
||||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent">
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
@ -40,11 +39,12 @@
|
|||||||
android:layout_alignParentTop="false"
|
android:layout_alignParentTop="false"
|
||||||
android:layout_below="@+id/infoTextView"
|
android:layout_below="@+id/infoTextView"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:contentDescription="Status Icon" />
|
android:contentDescription="Status icon" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
|
||||||
android:id="@+id/installProgressBar"
|
android:id="@+id/installProgressBar"
|
||||||
android:layout_width="40dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_below="@+id/fwappStatusIcon"
|
android:layout_below="@+id/fwappStatusIcon"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
@ -67,12 +67,20 @@
|
|||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
android:text="@string/appinstaller_install" />
|
android:text="@string/appinstaller_install" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/installProgressText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/installProgressBar"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/detailsListView"
|
android:id="@+id/detailsListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="false"
|
android:layout_alignParentEnd="false"
|
||||||
android:layout_below="@+id/installButton" />
|
android:layout_below="@+id/installProgressText" />
|
||||||
|
|
||||||
<android.widget.Space
|
<android.widget.Space
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
Loading…
Reference in New Issue
Block a user