diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfb5257d1..0279742fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,11 @@
###Changelog
####Next Release (probably 0.3.0)
-* Fix installation problems with certain .pbw files
-* Volume control for Pebble
+* Pebble: Firmware installation (USE AT YOUR OWN RISK)
+* Pebble: Fix installation problems with certain .pbw files
+* Pebble: Volume control
* Add icon for activity tracker apps (icon by xphnx)
+* Let the application quit when in reconnecting state
####Version 0.2.0
* Experimental pbw installation support (watchfaces/apps)
diff --git a/README.md b/README.md
index 3501eeb72..fb3cc2557 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,8 @@ Features:
* Apollo playback info (artist, album, track)
* Music control: play/pause, next track, previous track, volume up, volume down
* List and remove installed apps/watchfaces
-* Install .pbw files (EXPERMIENTAL)
+* Install .pbw files
+* Install firmware from .pbz files (EXPERIMENTAL)
How to use:
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f0793b545..583561c3c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -52,6 +52,7 @@
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PBWReader.java
index 03266c5f7..2fabdded4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PBWReader.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PBWReader.java
@@ -14,6 +14,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -21,11 +23,28 @@ import nodomain.freeyourgadget.gadgetbridge.GBDeviceApp;
public class PBWReader {
private static final String TAG = PebbleIoThread.class.getSimpleName();
+ private static final HashMap appFileTypesMap;
+
+ static {
+ appFileTypesMap = new HashMap();
+ appFileTypesMap.put("application", PebbleProtocol.PUTBYTES_TYPE_BINARY);
+ appFileTypesMap.put("resources", PebbleProtocol.PUTBYTES_TYPE_RESOURCES);
+ appFileTypesMap.put("worker", PebbleProtocol.PUTBYTES_TYPE_WORKER);
+ }
+
+ private static final HashMap fwFileTypesMap;
+
+ static {
+ fwFileTypesMap = new HashMap();
+ fwFileTypesMap.put("firmware", PebbleProtocol.PUTBYTES_TYPE_FIRMWARE);
+ fwFileTypesMap.put("resources", PebbleProtocol.PUTBYTES_TYPE_SYSRESOURCES);
+ }
- private GBDeviceApp app;
private final Uri uri;
private final ContentResolver cr;
+ private GBDeviceApp app;
private ArrayList pebbleInstallables;
+ private boolean isFirmware = false;
public PBWReader(Uri uri, Context context) {
this.uri = uri;
@@ -60,35 +79,32 @@ public class PBWReader {
String jsonString = baos.toString();
try {
JSONObject json = new JSONObject(jsonString);
- JSONObject application = json.getJSONObject("application");
+ String[] searchJSON;
+ HashMap fileTypeMap;
- String name = application.getString("name");
- int size = application.getInt("size");
- long crc = application.getLong("crc");
- pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_BINARY));
- Log.i(TAG, "found app binary to install: " + name);
try {
- JSONObject resources = json.getJSONObject("resources");
- name = resources.getString("name");
- size = resources.getInt("size");
- crc = resources.getLong("crc");
- pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_RESOURCES));
- Log.i(TAG, "found resources to install: " + name);
+ json.getJSONObject("firmware");
+ fileTypeMap = fwFileTypesMap;
+ isFirmware = true;
} catch (JSONException e) {
- // no resources, that is no problem
+ fileTypeMap = appFileTypesMap;
+ isFirmware = false;
}
- try {
- JSONObject worker = json.getJSONObject("worker");
- name = worker.getString("name");
- size = worker.getInt("size");
- crc = worker.getLong("crc");
- pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_WORKER));
- Log.i(TAG, "found worker to install: " + name);
- } catch (JSONException e) {
- // no worker, that is no problem
+ for (Map.Entry entry : fileTypeMap.entrySet()) {
+ try {
+ JSONObject jo = json.getJSONObject(entry.getKey());
+ String name = jo.getString("name");
+ int size = jo.getInt("size");
+ long crc = jo.getLong("crc");
+ byte type = entry.getValue();
+ pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, type));
+ Log.i(TAG, "found file to install: " + name);
+ } catch (JSONException e) {
+ // not fatal
+ }
}
} catch (JSONException e) {
- // no application, that is a problem
+ // no JSON at all that is a problem
e.printStackTrace();
break;
}
@@ -126,6 +142,10 @@ public class PBWReader {
}
}
+ protected boolean isFirmware() {
+ return isFirmware;
+ }
+
public GBDeviceApp getGBDeviceApp() {
return app;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleAppInstallerActivity.java
index 3dff5f618..f6d8aa186 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleAppInstallerActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleAppInstallerActivity.java
@@ -35,8 +35,13 @@ public class PebbleAppInstallerActivity extends Activity {
PBWReader pbwReader = new PBWReader(uri, getApplicationContext());
GBDeviceApp app = pbwReader.getGBDeviceApp();
- if (pbwReader != null && app != null) {
- debugTextView.setText("THIS IS HIGHLY EXPERIMENTAL PROCEED AT YOUR OWN RISK\n\n\n" + app.getName() + " Version " + app.getVersion() + " by " + app.getCreator() + "\n");
+ if (pbwReader != null) {
+ if (pbwReader.isFirmware()) {
+ debugTextView.setText("YOUR ARE TRYING TO INSTALL A FIRMWARE, PROCEED AT YOUR OWN RISK, MAKE SURE THIS FIRMWARE IS FOR YOUR PEBBLE REVISION, THERE ARE NO CHECKS.\n\n\n");
+
+ } else if (app != null) {
+ debugTextView.setText("You are about to install the following app:\n\n\n" + app.getName() + " Version " + app.getVersion() + " by " + app.getCreator() + "\n");
+ }
installButton.setEnabled(true);
installButton.setOnClickListener(new View.OnClickListener() {
@Override
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleIoThread.java
index 30d8a806a..29aad778e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleIoThread.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleIoThread.java
@@ -121,16 +121,21 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
break;
case APP_START_INSTALL:
- Log.i(TAG, "start installing app binary");
if (mPBWReader == null) {
mPBWReader = new PBWReader(mInstallURI, getContext());
mPebbleInstallables = mPBWReader.getPebbleInstallables();
mCurrentInstallableIndex = 0;
+ if (mPBWReader.isFirmware()) {
+ writeInstallApp(mPebbleProtocol.encodeInstallFirmwareStart());
+ mInstallSlot = 0;
+ Log.i(TAG, "starting firmware installation");
+ }
}
+ Log.i(TAG, "start installing app binary");
PebbleInstallable pi = mPebbleInstallables[mCurrentInstallableIndex];
mZis = mPBWReader.getInputStreamFile(pi.getFileName());
mCRC = pi.getCRC();
- int binarySize = pi.getFileSize(); // TODO: use for progrssbar
+ int binarySize = pi.getFileSize(); // TODO: use for progressbar
writeInstallApp(mPebbleProtocol.encodeUploadStart(pi.getType(), (byte) mInstallSlot, binarySize));
mInstallState = PebbleAppInstallState.APP_WAIT_TOKEN;
break;
@@ -174,7 +179,12 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
break;
case APP_REFRESH:
- writeInstallApp(mPebbleProtocol.encodeAppRefresh(mInstallSlot));
+ if (mPBWReader.isFirmware()) {
+ writeInstallApp(mPebbleProtocol.encodeInstallFirmwareComplete());
+ finishInstall(false);
+ } else {
+ writeInstallApp(mPebbleProtocol.encodeAppRefresh(mInstallSlot));
+ }
break;
default:
break;
@@ -239,7 +249,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
gbDevice.sendDeviceUpdateIntent(getContext());
GB.updateNotification("connection lost, trying to reconnect", getContext());
- while (mConnectionAttempts++ < 10) {
+ while (mConnectionAttempts++ < 10 && !mQuit) {
Log.i(TAG, "Trying to reconnect (attempt " + mConnectionAttempts + ")");
mIsConnected = connect(gbDevice.getAddress());
if (mIsConnected)
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleProtocol.java
index 014a62468..1a3ac22c6 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleProtocol.java
@@ -96,6 +96,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final byte PUTBYTES_TYPE_FILE = 6;
public static final byte PUTBYTES_TYPE_WORKER = 7;
+ private final byte SYSTEMMESSAGE_FIRMWARESTART = 1;
+ private final byte SYSTEMMESSAGE_FIRMWARECOMPLETE = 2;
+ private final byte SYSTEMMESSAGE_FIRMWAREFAIL = 3;
+
static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains
static final byte PHONEVERSION_APPVERSION_MAJOR = 2;
static final byte PHONEVERSION_APPVERSION_MINOR = 3;
@@ -132,6 +136,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final short LENGTH_UPLOADCOMMIT = 9;
static final short LENGTH_UPLOADCOMPLETE = 5;
static final short LENGTH_UPLOADCANCEL = 5;
+ static final short LENGTH_SYSTEMMESSAGE = 2;
private static byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) {
// Calculate length first
@@ -360,6 +365,30 @@ public class PebbleProtocol extends GBDeviceProtocol {
return buf.array();
}
+ private byte[] encodeSystemMessage(byte systemMessage) {
+ ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_SYSTEMMESSAGE);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putShort(LENGTH_SYSTEMMESSAGE);
+ buf.putShort(ENDPOINT_SYSTEMMESSAGE);
+ buf.put((byte) 0);
+ buf.put(systemMessage);
+ return buf.array();
+
+ }
+
+ public byte[] encodeInstallFirmwareStart() {
+ return encodeSystemMessage(SYSTEMMESSAGE_FIRMWARESTART);
+ }
+
+ public byte[] encodeInstallFirmwareComplete() {
+ return encodeSystemMessage(SYSTEMMESSAGE_FIRMWARECOMPLETE);
+ }
+
+ public byte[] encodeInstallFirmwareError() {
+ return encodeSystemMessage(SYSTEMMESSAGE_FIRMWAREFAIL);
+ }
+
+
public byte[] encodeAppRefresh(int index) {
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REFRESHAPP);
buf.order(ByteOrder.BIG_ENDIAN);