mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 12:26:48 +01:00
Pebble: Firmware upgrade support
This commit is contained in:
parent
235c603d92
commit
7c60e4b595
@ -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)
|
||||
|
@ -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:
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
<data android:host="*" />
|
||||
<data android:scheme="file" />
|
||||
<data android:pathPattern=".*\\.pbw" />
|
||||
<data android:pathPattern=".*\\.pbz" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
@ -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<String, Byte> appFileTypesMap;
|
||||
|
||||
static {
|
||||
appFileTypesMap = new HashMap<String, Byte>();
|
||||
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<String, Byte> fwFileTypesMap;
|
||||
|
||||
static {
|
||||
fwFileTypesMap = new HashMap<String, Byte>();
|
||||
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<PebbleInstallable> 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<String, Byte> 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;
|
||||
}
|
||||
for (Map.Entry<String, Byte> entry : fileTypeMap.entrySet()) {
|
||||
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);
|
||||
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) {
|
||||
// no worker, that is no problem
|
||||
// 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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user