mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 20:36:51 +01:00
Pebble: working on app installation (WIP, do not use)
This commit is contained in:
parent
1766c82ab8
commit
2c1cacedb3
@ -40,7 +40,7 @@
|
||||
android:value=".ControlCenter" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".AppInstallerActivity"
|
||||
android:name=".pebble.PebbleAppInstallerActivity"
|
||||
android:label="@string/title_activity_appinstaller">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
|
@ -1,98 +0,0 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
|
||||
public class AppInstallerActivity extends Activity {
|
||||
|
||||
private final String TAG = this.getClass().getSimpleName();
|
||||
|
||||
TextView debugTextView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_appinstaller);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
debugTextView = (TextView) findViewById(R.id.debugTextView);
|
||||
debugTextView.setText("contents:\n");
|
||||
Uri uri = getIntent().getData();
|
||||
|
||||
ContentResolver cr = getContentResolver();
|
||||
InputStream fin = null;
|
||||
try {
|
||||
fin = cr.openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
ZipInputStream zis = new ZipInputStream(fin);
|
||||
ZipEntry ze = null;
|
||||
GBDeviceApp app;
|
||||
try {
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
if (ze.getName().equals("appinfo.json")) {
|
||||
long bytes = ze.getSize();
|
||||
if (bytes > 8192) // that should be too much
|
||||
break;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
String jsonString = baos.toString();
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonString);
|
||||
String appName = json.getString("shortName");
|
||||
String appCreator = json.getString("companyName");
|
||||
String appVersion = json.getString("versionLabel");
|
||||
if (appName != null && appCreator != null && appVersion != null) {
|
||||
debugTextView.setText("This is just a test, you cant install anything yet \n\n" + appName + " Version " + appVersion + " by " + appCreator + "\n");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
@ -29,7 +29,9 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.pebble.PBWReader;
|
||||
import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommandAppInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommandAppManagementResult;
|
||||
@ -63,8 +65,10 @@ public class BluetoothCommunicationService extends Service {
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.request_appinfo";
|
||||
public static final String ACTION_DELETEAPP
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.deleteapp";
|
||||
public static final String ACTION_INSTALL_PEBBLEAPP
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.install_pebbbleapp";
|
||||
|
||||
private static final String TAG = "BluetoothCommunicationService";
|
||||
private static final String TAG = "CommunicationService";
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private BluetoothAdapter mBtAdapter = null;
|
||||
private BluetoothSocket mBtSocket = null;
|
||||
@ -161,6 +165,7 @@ public class BluetoothCommunicationService extends Service {
|
||||
case APP_INFO:
|
||||
Log.i(TAG, "Got command for APP_INFO");
|
||||
GBDeviceCommandAppInfo appInfoCmd = (GBDeviceCommandAppInfo) deviceCmd;
|
||||
mGBDevice.setFreeAppSlot(appInfoCmd.freeSlot);
|
||||
Intent appInfoIntent = new Intent(AppManagerActivity.ACTION_REFRESH_APPLIST);
|
||||
int appCount = appInfoCmd.apps.length;
|
||||
appInfoIntent.putExtra("app_count", appCount);
|
||||
@ -189,6 +194,20 @@ public class BluetoothCommunicationService extends Service {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case INSTALL:
|
||||
switch (appMgmtRes.result) {
|
||||
case FAILURE:
|
||||
Log.i(TAG, "failure installing app"); // TODO: report to Installer
|
||||
break;
|
||||
case SUCCESS:
|
||||
if (mGBDevice.getType() == GBDevice.Type.PEBBLE) {
|
||||
((PebbleIoThread) mGBDeviceIoThread).setToken(appMgmtRes.token);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -329,6 +348,12 @@ public class BluetoothCommunicationService extends Service {
|
||||
int id = intent.getIntExtra("app_id", -1);
|
||||
int index = intent.getIntExtra("app_index", -1);
|
||||
mGBDeviceIoThread.write(mGBDeviceProtocol.encodeAppDelete(id, index));
|
||||
} else if (action.equals(ACTION_INSTALL_PEBBLEAPP)) {
|
||||
String uriString = intent.getStringExtra("app_uri");
|
||||
if (uriString != null && mGBDevice.getFreeAppSlot() != -1) {
|
||||
Log.i(TAG, "will try to install app in slot " + mGBDevice.getFreeAppSlot());
|
||||
((PebbleIoThread) mGBDeviceIoThread).installApp(Uri.parse(uriString));
|
||||
}
|
||||
} else if (action.equals(ACTION_START)) {
|
||||
startForeground(NOTIFICATION_ID, createNotification("Gadgetbridge running"));
|
||||
mStarted = true;
|
||||
@ -415,15 +440,37 @@ public class BluetoothCommunicationService extends Service {
|
||||
// implement connect() run() write() and quit() here
|
||||
}
|
||||
|
||||
private enum PebbleAppInstallState {
|
||||
UNKNOWN,
|
||||
APP_START_INSTALL,
|
||||
APP_WAIT_TOKEN,
|
||||
APP_UPLOAD_CHUNK,
|
||||
APP_UPLOAD_COMPLETE,
|
||||
RES_START_INSTALL,
|
||||
RES_WAIT_TOKEN,
|
||||
RES_UPLOAD_CHUNK,
|
||||
RES_UPLOAD_COMPLETE,
|
||||
}
|
||||
|
||||
private class PebbleIoThread extends GBDeviceIoThread {
|
||||
private final PebbleProtocol mmPebbleProtocol;
|
||||
private InputStream mmInStream = null;
|
||||
private OutputStream mmOutStream = null;
|
||||
private boolean mQuit = false;
|
||||
private boolean mmQuit = false;
|
||||
private boolean mmIsConnected = false;
|
||||
private boolean mmIsInstalling = false;
|
||||
private int mmConnectionAttempts = 0;
|
||||
|
||||
/* app installation */
|
||||
private Uri mmInstallURI = null;
|
||||
private PBWReader mmPBWReader = null;
|
||||
private int mmAppInstallToken = -1;
|
||||
private ZipInputStream mmZis = null;
|
||||
private PebbleAppInstallState mmInstallState = PebbleAppInstallState.UNKNOWN;
|
||||
|
||||
public PebbleIoThread(String btDeviceAddress) {
|
||||
super(btDeviceAddress);
|
||||
mmPebbleProtocol = (PebbleProtocol) mGBDeviceProtocol;
|
||||
}
|
||||
|
||||
private boolean connect(String btDeviceAddress) {
|
||||
@ -451,13 +498,48 @@ public class BluetoothCommunicationService extends Service {
|
||||
public void run() {
|
||||
mmIsConnected = connect(mmBtDeviceAddress);
|
||||
setReceiversEnableState(mmIsConnected); // enable/disable BroadcastReceivers
|
||||
mQuit = !mmIsConnected; // quit if not connected
|
||||
mmQuit = !mmIsConnected; // quit if not connected
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytes;
|
||||
|
||||
while (!mQuit) {
|
||||
while (!mmQuit) {
|
||||
try {
|
||||
if (mmIsInstalling) {
|
||||
switch (mmInstallState) {
|
||||
case APP_START_INSTALL:
|
||||
Log.i(TAG, "start installing app binary");
|
||||
mmPBWReader = new PBWReader(mmInstallURI, getApplicationContext());
|
||||
mmZis = mmPBWReader.getInputStreamAppBinary();
|
||||
int binarySize = mmPBWReader.getAppBinarySize();
|
||||
writeInstallApp(mmPebbleProtocol.encodeUploadStart(PebbleProtocol.PUTBYTES_TYPE_BINARY, mGBDevice.getFreeAppSlot(), binarySize), -1);
|
||||
mmInstallState = PebbleAppInstallState.APP_WAIT_TOKEN;
|
||||
break;
|
||||
case APP_WAIT_TOKEN:
|
||||
if (mmAppInstallToken != -1) {
|
||||
Log.i(TAG, "got token " + mmAppInstallToken);
|
||||
mmInstallState = PebbleAppInstallState.APP_UPLOAD_CHUNK;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case APP_UPLOAD_CHUNK:
|
||||
bytes = mmZis.read(buffer);
|
||||
|
||||
if (bytes != -1) {
|
||||
writeInstallApp(mmPebbleProtocol.encodeUploadChunk(mmAppInstallToken, buffer, bytes), -1);
|
||||
mmAppInstallToken = -1;
|
||||
mmInstallState = PebbleAppInstallState.APP_WAIT_TOKEN;
|
||||
} else {
|
||||
mmInstallState = PebbleAppInstallState.UNKNOWN;
|
||||
mmIsInstalling = false;
|
||||
mmZis = null;
|
||||
mmAppInstallToken = -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
bytes = mmInStream.read(buffer, 0, 4);
|
||||
if (bytes < 4)
|
||||
continue;
|
||||
@ -488,17 +570,17 @@ public class BluetoothCommunicationService extends Service {
|
||||
|
||||
if (length == 1 && endpoint == PebbleProtocol.ENDPOINT_PHONEVERSION) {
|
||||
Log.i(TAG, "Pebble asked for Phone/App Version - repLYING!");
|
||||
write(mGBDeviceProtocol.encodePhoneVersion(PebbleProtocol.PHONEVERSION_REMOTE_OS_ANDROID));
|
||||
write(mGBDeviceProtocol.encodeFirmwareVersionReq());
|
||||
write(mmPebbleProtocol.encodePhoneVersion(PebbleProtocol.PHONEVERSION_REMOTE_OS_ANDROID));
|
||||
write(mmPebbleProtocol.encodeFirmwareVersionReq());
|
||||
|
||||
// this does not really belong here, but since the pebble only asks for our version once it should do the job
|
||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(BluetoothCommunicationService.this);
|
||||
if (sharedPrefs.getBoolean("datetime_synconconnect", true)) {
|
||||
Log.i(TAG, "syncing time");
|
||||
write(mGBDeviceProtocol.encodeSetTime(-1));
|
||||
write(mmPebbleProtocol.encodeSetTime(-1));
|
||||
}
|
||||
} else if (endpoint != PebbleProtocol.ENDPOINT_DATALOG) {
|
||||
GBDeviceCommand deviceCmd = mGBDeviceProtocol.decodeResponse(buffer);
|
||||
GBDeviceCommand deviceCmd = mmPebbleProtocol.decodeResponse(buffer);
|
||||
if (deviceCmd == null) {
|
||||
Log.i(TAG, "unhandled message to endpoint " + endpoint + " (" + bytes + " bytes)");
|
||||
} else {
|
||||
@ -512,6 +594,7 @@ public class BluetoothCommunicationService extends Service {
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("socket closed")) { //FIXME: this does not feel right
|
||||
Log.i(TAG, e.getMessage());
|
||||
mGBDevice.setState(GBDevice.State.CONNECTING);
|
||||
sendDeviceUpdateIntent();
|
||||
updateNotification("connection lost, trying to reconnect");
|
||||
@ -527,7 +610,7 @@ public class BluetoothCommunicationService extends Service {
|
||||
mBtSocket = null;
|
||||
setReceiversEnableState(false);
|
||||
Log.i(TAG, "Bluetooth socket closed, will quit IO Thread");
|
||||
mQuit = true;
|
||||
mmQuit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -547,7 +630,8 @@ public class BluetoothCommunicationService extends Service {
|
||||
}
|
||||
|
||||
synchronized public void write(byte[] bytes) {
|
||||
if (mmIsConnected) {
|
||||
// block writes if app installation in in progress
|
||||
if (mmIsConnected && !mmIsInstalling) {
|
||||
try {
|
||||
mmOutStream.write(bytes);
|
||||
mmOutStream.flush();
|
||||
@ -556,8 +640,41 @@ public class BluetoothCommunicationService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
public void setToken(int token) {
|
||||
mmAppInstallToken = token;
|
||||
}
|
||||
|
||||
private void writeInstallApp(byte[] bytes, int length) {
|
||||
if (!mmIsInstalling) {
|
||||
return;
|
||||
}
|
||||
if (length == -1) {
|
||||
length = bytes.length;
|
||||
}
|
||||
Log.i(TAG, "got bytes for writeInstallApp()" + length);
|
||||
final char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
char[] hexChars = new char[length * 2];
|
||||
for (int j = 0; j < length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = hexArray[v >>> 4];
|
||||
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
Log.i(TAG, new String(hexChars));
|
||||
try {
|
||||
mmOutStream.write(bytes);
|
||||
mmOutStream.flush();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void installApp(Uri uri) {
|
||||
mmInstallState = PebbleAppInstallState.APP_START_INSTALL;
|
||||
mmIsInstalling = true;
|
||||
mmInstallURI = uri;
|
||||
}
|
||||
|
||||
public void quit() {
|
||||
mQuit = true;
|
||||
mmQuit = true;
|
||||
if (mBtSocket != null) {
|
||||
try {
|
||||
mBtSocket.close();
|
||||
|
@ -6,6 +6,7 @@ public class GBDevice {
|
||||
private final Type type;
|
||||
private String firmwareVersion = null;
|
||||
private State state = State.NOT_CONNECTED;
|
||||
private byte freeAppSlot = -1;
|
||||
|
||||
public GBDevice(String address, String name, Type type) {
|
||||
this.address = address;
|
||||
@ -61,6 +62,14 @@ public class GBDevice {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setFreeAppSlot(byte freeAppSlot) {
|
||||
this.freeAppSlot = freeAppSlot;
|
||||
}
|
||||
|
||||
public byte getFreeAppSlot() {
|
||||
return freeAppSlot;
|
||||
}
|
||||
|
||||
public enum State {
|
||||
NOT_CONNECTED,
|
||||
CONNECTING,
|
||||
|
@ -0,0 +1,154 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.pebble;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDeviceApp;
|
||||
|
||||
public class PBWReader {
|
||||
private GBDeviceApp app;
|
||||
private final Uri uri;
|
||||
private final ContentResolver cr;
|
||||
|
||||
public PBWReader(Uri uri, Context context) {
|
||||
this.uri = uri;
|
||||
cr = context.getContentResolver();
|
||||
|
||||
InputStream fin = null;
|
||||
try {
|
||||
fin = new BufferedInputStream(cr.openInputStream(uri));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
ZipInputStream zis = new ZipInputStream(fin);
|
||||
ZipEntry ze = null;
|
||||
try {
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
if (ze.getName().equals("appinfo.json")) {
|
||||
long bytes = ze.getSize();
|
||||
if (bytes > 8192) // that should be too much
|
||||
break;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
String jsonString = baos.toString();
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonString);
|
||||
String appName = json.getString("shortName");
|
||||
String appCreator = json.getString("companyName");
|
||||
String appVersion = json.getString("versionLabel");
|
||||
if (appName != null && appCreator != null && appVersion != null) {
|
||||
// FIXME: dont assume WATCHFACE
|
||||
app = new GBDeviceApp(-1, -1, appName, appCreator, appVersion, GBDeviceApp.Type.WATCHFACE);
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public GBDeviceApp getGBDeviceApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStreamFile(String filename) {
|
||||
InputStream fin = null;
|
||||
try {
|
||||
fin = new BufferedInputStream(cr.openInputStream(uri));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
ZipInputStream zis = new ZipInputStream(fin);
|
||||
ZipEntry ze = null;
|
||||
try {
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
if (ze.getName().equals(filename)) {
|
||||
return zis;
|
||||
}
|
||||
}
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getFileSize(String filename) {
|
||||
InputStream fin = null;
|
||||
try {
|
||||
fin = new BufferedInputStream(cr.openInputStream(uri));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZipInputStream zis = new ZipInputStream(fin);
|
||||
ZipEntry ze = null;
|
||||
try {
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
if (ze.getName().equals(filename)) {
|
||||
return (int) ze.getSize();
|
||||
}
|
||||
}
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStreamAppBinary() {
|
||||
return getInputStreamFile("pebble-app.bin");
|
||||
}
|
||||
|
||||
public int getAppBinarySize() {
|
||||
return getFileSize("pebble-app.bin");
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStreamAppWorker() {
|
||||
return getInputStreamFile("pebble-worker.bin");
|
||||
}
|
||||
|
||||
public int getAppWorkerSize() {
|
||||
return getFileSize("pebble-worker.bin");
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStreamAppResources() {
|
||||
return getInputStreamFile("app_resources.pbpack");
|
||||
}
|
||||
|
||||
public int getAppResourcesSize() {
|
||||
return getFileSize("pebble-resources.pbpack");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.pebble;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.BluetoothCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBDeviceApp;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
|
||||
public class PebbleAppInstallerActivity extends Activity {
|
||||
|
||||
private final String TAG = this.getClass().getSimpleName();
|
||||
|
||||
TextView debugTextView;
|
||||
Button installButton;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_appinstaller);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
debugTextView = (TextView) findViewById(R.id.debugTextView);
|
||||
installButton = (Button) findViewById(R.id.installButton);
|
||||
|
||||
debugTextView.setText("contents:\n");
|
||||
final Uri uri = getIntent().getData();
|
||||
PBWReader pbwReader = new PBWReader(uri, getApplicationContext());
|
||||
|
||||
GBDeviceApp app = pbwReader.getGBDeviceApp();
|
||||
ZipInputStream zis = pbwReader.getInputStreamAppBinary();
|
||||
/*
|
||||
STM32CRC stm32crc = new STM32CRC();
|
||||
byte[] buffer = new byte[1000];
|
||||
int count = 0;
|
||||
try {
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
stm32crc.addData(buffer, count);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.i("TEST", "STMCRC32: " + stm32crc.getResult());
|
||||
*/
|
||||
if (pbwReader != null && app != null) {
|
||||
debugTextView.setText("This is just a test, you cant install anything yet \n\n" + app.getName() + " Version " + app.getVersion() + " by " + app.getCreator() + "\n");
|
||||
installButton.setEnabled(true);
|
||||
installButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent startIntent = new Intent(PebbleAppInstallerActivity.this, BluetoothCommunicationService.class);
|
||||
startIntent.setAction(BluetoothCommunicationService.ACTION_INSTALL_PEBBLEAPP);
|
||||
startIntent.putExtra("app_uri", uri.toString());
|
||||
startService(startIntent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBDeviceApp;
|
||||
|
||||
public class GBDeviceCommandAppInfo extends GBDeviceCommand {
|
||||
public GBDeviceApp apps[];
|
||||
public byte freeSlot = -1;
|
||||
|
||||
public GBDeviceCommandAppInfo() {
|
||||
commandClass = CommandClass.APP_INFO;
|
||||
|
@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.protocol;
|
||||
public class GBDeviceCommandAppManagementResult extends GBDeviceCommand {
|
||||
public Result result = Result.UNKNOWN;
|
||||
public CommandType type = CommandType.UNKNOWN;
|
||||
public int token = -1;
|
||||
|
||||
public GBDeviceCommandAppManagementResult() {
|
||||
commandClass = CommandClass.APP_MANAGEMENT_RES;
|
||||
@ -10,12 +11,14 @@ public class GBDeviceCommandAppManagementResult extends GBDeviceCommand {
|
||||
|
||||
public enum CommandType {
|
||||
UNKNOWN,
|
||||
INSTALL,
|
||||
DELETE,
|
||||
}
|
||||
|
||||
public enum Result {
|
||||
UNKNOWN,
|
||||
SUCCESS,
|
||||
ACKNOLEDGE,
|
||||
FAILURE,
|
||||
}
|
||||
}
|
||||
|
@ -74,11 +74,19 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
|
||||
static final int APPMANAGER_RES_SUCCESS = 1;
|
||||
|
||||
static final short LENGTH_PREFIX = 4;
|
||||
static final short LENGTH_SETTIME = 5;
|
||||
static final short LENGTH_REMOVEAPP = 9;
|
||||
static final short LENGTH_PHONEVERSION = 17;
|
||||
static final byte PUTBYTES_INIT = 1;
|
||||
static final byte PUTBYTES_SEND = 2;
|
||||
static final byte PUTBYTES_COMMIT = 3;
|
||||
static final byte PUTBYTES_ABORT = 4;
|
||||
static final byte PUTBYTES_COMPLETE = 5;
|
||||
|
||||
static final byte PUTBYTES_TYPE_FIRMWARE = 1;
|
||||
static final byte PUTBYTES_TYPE_RECOVERY = 2;
|
||||
static final byte PUTBYTES_TYPE_SYSRESOURCES = 3;
|
||||
static final byte PUTBYTES_TYPE_RESOURCES = 4;
|
||||
public static final byte PUTBYTES_TYPE_BINARY = 5;
|
||||
static final byte PUTBYTES_TYPE_FILE = 6;
|
||||
static final byte PUTBYTES_TYPE_WORKER = 7;
|
||||
|
||||
static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains
|
||||
static final byte PHONEVERSION_APPVERSION_MAJOR = 2;
|
||||
@ -104,6 +112,14 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
static final byte PHONEVERSION_REMOTE_OS_LINUX = 4;
|
||||
static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5;
|
||||
|
||||
|
||||
static final short LENGTH_PREFIX = 4;
|
||||
static final short LENGTH_SETTIME = 5;
|
||||
static final short LENGTH_REMOVEAPP = 9;
|
||||
static final short LENGTH_PHONEVERSION = 17;
|
||||
static final short LENGTH_UPLOADSTART = 7;
|
||||
static final short LENGTH_UPLOADCHUNK = 9;
|
||||
|
||||
private static byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) {
|
||||
// Calculate length first
|
||||
int length = LENGTH_PREFIX + 1;
|
||||
@ -265,6 +281,31 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
/* pebble specific install methods */
|
||||
public byte[] encodeUploadStart(byte type, byte index, int size) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADSTART);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
buf.putShort(LENGTH_UPLOADSTART);
|
||||
buf.putShort(ENDPOINT_PUTBYTES);
|
||||
buf.put(PUTBYTES_INIT);
|
||||
buf.putInt(size);
|
||||
buf.put(type);
|
||||
buf.put(index);
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
public byte[] encodeUploadChunk(int token, byte[] buffer, int size) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADCHUNK + size);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
buf.putShort((short) (LENGTH_UPLOADCHUNK + size));
|
||||
buf.putShort(ENDPOINT_PUTBYTES);
|
||||
buf.put(PUTBYTES_SEND);
|
||||
buf.putInt(token);
|
||||
buf.putInt(size);
|
||||
buf.put(buffer, 0, size);
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
public GBDeviceCommand decodeResponse(byte[] responseData) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(responseData);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
@ -322,15 +363,17 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
switch (pebbleCmd) {
|
||||
case APPMANAGER_GETAPPBANKSTATUS:
|
||||
GBDeviceCommandAppInfo appInfoCmd = new GBDeviceCommandAppInfo();
|
||||
int banks = buf.getInt();
|
||||
int banksUsed = buf.getInt();
|
||||
int slotCount = buf.getInt();
|
||||
int slotsUsed = buf.getInt();
|
||||
byte[] appName = new byte[32];
|
||||
byte[] appCreator = new byte[32];
|
||||
appInfoCmd.apps = new GBDeviceApp[banksUsed];
|
||||
appInfoCmd.apps = new GBDeviceApp[slotsUsed];
|
||||
boolean[] slotInUse = new boolean[slotCount];
|
||||
|
||||
for (int i = 0; i < banksUsed; i++) {
|
||||
for (int i = 0; i < slotsUsed; i++) {
|
||||
int id = buf.getInt();
|
||||
int index = buf.getInt();
|
||||
slotInUse[index] = true;
|
||||
buf.get(appName, 0, 32);
|
||||
buf.get(appCreator, 0, 32);
|
||||
int flags = buf.getInt();
|
||||
@ -347,6 +390,13 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
Short appVersion = buf.getShort();
|
||||
appInfoCmd.apps[i] = new GBDeviceApp(id, index, new String(appName).trim(), new String(appCreator).trim(), appVersion.toString(), appType);
|
||||
}
|
||||
for (int i = 0; i < slotCount; i++) {
|
||||
if (!slotInUse[i]) {
|
||||
appInfoCmd.freeSlot = (byte) i;
|
||||
Log.i(TAG, "found free slot " + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmd = appInfoCmd;
|
||||
break;
|
||||
case APPMANAGER_REMOVEAPP:
|
||||
@ -369,6 +419,21 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ENDPOINT_PUTBYTES:
|
||||
GBDeviceCommandAppManagementResult installRes = new GBDeviceCommandAppManagementResult();
|
||||
installRes.type = GBDeviceCommandAppManagementResult.CommandType.INSTALL;
|
||||
switch (pebbleCmd) {
|
||||
case PUTBYTES_INIT:
|
||||
installRes.token = buf.getInt();
|
||||
installRes.result = GBDeviceCommandAppManagementResult.Result.SUCCESS;
|
||||
break;
|
||||
default:
|
||||
installRes.token = buf.getInt();
|
||||
installRes.result = GBDeviceCommandAppManagementResult.Result.FAILURE;
|
||||
break;
|
||||
}
|
||||
cmd = installRes;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -6,11 +6,20 @@
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.AppInstallerActivity">
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.pebble.PebbleAppInstallerActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/debugTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Install"
|
||||
android:id="@+id/installButton"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:enabled="false" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
Loading…
Reference in New Issue
Block a user