diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index 1b3901dc7..db6b1ec24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Lem Dulfo + Gobbetti, Lem Dulfo, Taavi Eomäe This file is part of Gadgetbridge. @@ -66,6 +66,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal private boolean mayConnect; private ProgressBar progressBar; + private TextView progressText; private ListView itemListView; private final List items = new ArrayList<>(); private ItemWithDetailsAdapter itemAdapter; @@ -94,6 +95,23 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal 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)) { String message = intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE); 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() { mayConnect = false; // only do that once per #onCreate GBApplication.deviceService().connect(device); @@ -148,6 +180,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal fwAppInstallTextView = findViewById(R.id.infoTextView); installButton = findViewById(R.id.installButton); progressBar = findViewById(R.id.installProgressBar); + progressText = findViewById(R.id.installProgressText); detailsListView = findViewById(R.id.detailsListView); detailsAdapter = new ItemWithDetailsAdapter(this, details); detailsAdapter.setSize(ItemWithDetailsAdapter.SIZE_SMALL); @@ -157,6 +190,9 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal IntentFilter filter = new IntentFilter(); filter.addAction(GBDevice.ACTION_DEVICE_CHANGED); 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); installButton.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeDFUService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeDFUService.java new file mode 100644 index 000000000..0cd6761f0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeDFUService.java @@ -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 . */ +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 getNotificationTarget() { + return FwAppInstallerActivity.class; + } + + @Override + protected boolean isDebug() { + return BuildConfig.DEBUG; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeInstallHandler.java new file mode 100644 index 000000000..d3e7a6632 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeInstallHandler.java @@ -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 . */ +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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java index 1c07cd663..4c8554701 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Andreas Shimokawa +/* Copyright (C) 2020 Andreas Shimokawa, Taavi Eomäe This file is part of Gadgetbridge. @@ -55,7 +55,8 @@ public class PineTimeJFCoordinator extends AbstractDeviceCoordinator { @Override public InstallHandler findInstallHandler(Uri uri, Context context) { - return null; + PineTimeInstallHandler handler = new PineTimeInstallHandler(uri, context); + return handler.isValid() ? handler : null; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java index 1097a1072..687fcae9c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java @@ -20,11 +20,13 @@ import android.os.Parcel; import android.os.Parcelable; import java.text.Collator; +import java.util.Objects; public class GenericItem implements ItemWithDetails { private String name; private String details; private int icon; + private boolean warning = false; public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -56,9 +58,9 @@ public class GenericItem implements ItemWithDetails { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(getName()); - dest.writeString(getDetails()); - dest.writeInt(getIcon()); + dest.writeString(name); + dest.writeString(details); + dest.writeInt(icon); } public void setName(String name) { @@ -66,6 +68,9 @@ public class GenericItem implements ItemWithDetails { } public void setDetails(String details) { + if (details.equals("(Unknown version)")) { + this.warning = true; + } this.details = details; } @@ -73,6 +78,14 @@ public class GenericItem implements ItemWithDetails { this.icon = icon; } + public boolean getWarning() { + return this.warning; + } + + public void setWarning(boolean enable) { + this.warning = enable; + } + @Override public String getName() { return name; @@ -95,32 +108,36 @@ public class GenericItem implements ItemWithDetails { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (this == o) { + return true; + } GenericItem that = (GenericItem) o; - return !(getName() != null ? !getName().equals(that.getName()) : that.getName() != null); - + return Objects.equals(name, that.name); } @Override public int hashCode() { - return getName() != null ? getName().hashCode() : 0; + return name != null ? name.hashCode() : 0; } @Override public int compareTo(ItemWithDetails another) { - if (getName().equals(another.getName())) { + if (name.equals(another.getName())) { return 0; } - if (getName() == null) { + if (name == null) { return +1; } else if (another.getName() == null) { return -1; } - return Collator.getInstance().compare(getName(), another.getName()); + return Collator.getInstance().compare(name, another.getName()); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java index 62f50dc69..9006d978a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java @@ -20,6 +20,9 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; import android.net.Uri; +import android.widget.Toast; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,11 +30,19 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.Locale; 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.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.model.Alarm; 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.NewAlert; 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.util.GB; -public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { - +public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuLogListener { private static final Logger LOG = LoggerFactory.getLogger(PineTimeJFSupport.class); - private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); - private final DeviceInfoProfile deviceInfoProfile; /** * These are used to keep track when long strings haven't changed, @@ -70,6 +80,126 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { String lastTrack; String lastArtist; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + + private final DeviceInfoProfile 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() { super(LOG); addSupportedService(GattService.UUID_SERVICE_ALERT_NOTIFICATION); @@ -92,6 +222,32 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { 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 public boolean useAutoConnect() { return false; @@ -113,7 +269,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { @Override 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(); byte[] bytes = BLETypeConversions.calendarToRawBytes(now); 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 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 @@ -196,11 +380,14 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { @Override 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 public void onSetConstantVibration(int intensity) { + } @Override @@ -268,7 +455,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport { builder.queue(getQueue()); } 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()); } 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(); 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); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index 19dca62e0..cd587ffda 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -80,6 +80,14 @@ public class GB { public static final String DISPLAY_MESSAGE_DURATION = "duration"; 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) { Intent notificationIntent = new Intent(context, ControlCenterv2.class); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK diff --git a/app/src/main/res/layout/activity_appinstaller.xml b/app/src/main/res/layout/activity_appinstaller.xml index 59a91a97d..26378125a 100644 --- a/app/src/main/res/layout/activity_appinstaller.xml +++ b/app/src/main/res/layout/activity_appinstaller.xml @@ -3,8 +3,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent"> - + android:contentDescription="Status icon" /> + + + android:layout_below="@+id/installProgressText" />