1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-08 19:27:04 +01:00

Huawei: Basic support for the installation of the applications

This commit is contained in:
Me7c7 2024-08-29 17:23:42 +03:00
parent d75ae6b54d
commit e54fd95a8b
15 changed files with 725 additions and 62 deletions

View File

@ -0,0 +1,65 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
import java.nio.ByteBuffer;
import java.util.ArrayList;
public class HuaweiAppParser {
public static class AppFileEntry {
public String filename;
public String folder;
public String unknown;
public byte[] content;
}
private String packageName;
private final ArrayList<AppFileEntry> entries = new ArrayList<>();
public String getPackageName() {
return packageName;
}
public byte[] getEntryContent(String filename) {
for(AppFileEntry en: entries) {
if(en.filename.equals(filename)) {
return en.content;
}
}
return null;
}
private byte[] readData(ByteBuffer data) {
int len = data.getInt();
byte[] newPayload = new byte[len];
data.get(newPayload, 0, len);
return newPayload;
}
private String readString(ByteBuffer data) {
return new String(readData(data));
}
public void parseData(byte[] in) throws Exception {
ByteBuffer data = ByteBuffer.wrap(in);
byte magic = data.get();
if(magic != (byte)0xbe){
throw new Exception("Invalid magic");
}
this.packageName = readString(data);
while(data.remaining() > 0) {
int pos = data.position();
byte a = data.get();
if(a != 0x00)
break;
data.position(pos);
AppFileEntry ent = new AppFileEntry();
ent.filename = readString(data);
ent.folder = readString(data);
ent.unknown = readString(data);
ent.content = readData(data);
entries.add(ent);
}
}
}

View File

@ -22,7 +22,6 @@ import android.content.SharedPreferences;
import android.net.Uri;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@ -37,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActi
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications.NotificationConstraintsType;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
@ -59,6 +59,8 @@ public class HuaweiCoordinator {
private Watchface.WatchfaceDeviceParams watchfaceDeviceParams;
private App.AppDeviceParams appDeviceParams;
private final HuaweiCoordinatorSupplier parent;
private boolean transactionCrypted=true;
@ -405,6 +407,8 @@ public class HuaweiCoordinator {
public boolean supportsWatchfaceParams(){ return supportsCommandForService(0x27, 0x01);}
public boolean supportsAppParams(){ return supportsCommandForService(0x2a, 0x06);}
public boolean supportsWeather() {
return supportsCommandForService(0x0f, 0x01);
}
@ -584,20 +588,26 @@ public class HuaweiCoordinator {
this.watchfaceDeviceParams = watchfaceDeviceParams;
}
public void setAppDeviceParams(App.AppDeviceParams appDeviceParams) {
this.appDeviceParams = appDeviceParams;
}
public App.AppDeviceParams getAppDeviceParams() {
return appDeviceParams;
}
public Class<? extends Activity> getAppManagerActivity() {
return AppManagerActivity.class;
}
public boolean getSupportsAppListFetching() {
return true;
}
public boolean getSupportsAppListFetching() { return true; }
public boolean getSupportsAppsManagement(GBDevice device) {
return true;
}
public boolean getSupportsInstalledAppManagement(GBDevice device) {
return false;
return this.supportsAppParams(); // NOTE: this check can be incorrect. But looks like it works
}
public boolean getSupportsCachedAppManagement(GBDevice device) {

View File

@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiAppManager;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiFwHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWatchfaceManager;
@ -56,57 +57,108 @@ public class HuaweiInstallHandler implements InstallHandler {
return;
}
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
if(helper.isWatchface()) {
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
HuaweiWatchfaceManager.WatchfaceDescription description = helper.getWatchfaceDescription();
HuaweiWatchfaceManager.WatchfaceDescription description = helper.getWatchfaceDescription();
HuaweiWatchfaceManager.Resolution resolution = new HuaweiWatchfaceManager.Resolution();
String deviceScreen = String.format("%d*%d",huaweiCoordinatorSupplier.getHuaweiCoordinator().getHeight(),
huaweiCoordinatorSupplier.getHuaweiCoordinator().getWidth());
this.valid = resolution.isValid(description.screen, deviceScreen);
HuaweiWatchfaceManager.Resolution resolution = new HuaweiWatchfaceManager.Resolution();
String deviceScreen = String.format("%d*%d", huaweiCoordinatorSupplier.getHuaweiCoordinator().getHeight(),
huaweiCoordinatorSupplier.getHuaweiCoordinator().getWidth());
this.valid = resolution.isValid(description.screen, deviceScreen);
installActivity.setInstallEnabled(true);
installActivity.setInstallEnabled(true);
GenericItem installItem = new GenericItem();
GenericItem installItem = new GenericItem();
if (helper.getWatchfacePreviewBitmap() != null) {
installItem.setPreview(helper.getWatchfacePreviewBitmap());
if (helper.getWatchfacePreviewBitmap() != null) {
installItem.setPreview(helper.getWatchfacePreviewBitmap());
}
installItem.setName(description.title);
installActivity.setInstallItem(installItem);
if (device.isBusy()) {
LOG.error("Firmware cannot be installed (device busy)");
installActivity.setInfoText("Firmware cannot be installed (device busy)");
installActivity.setInfoText(device.getBusyTask());
installActivity.setInstallEnabled(false);
return;
}
if (!device.isConnected()) {
LOG.error("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInfoText("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInstallEnabled(false);
return;
}
if (!this.valid) {
LOG.error("Watchface cannot be installed");
installActivity.setInfoText(context.getString(R.string.watchface_resolution_doesnt_match,
resolution.screenByThemeVersion(description.screen), deviceScreen));
installActivity.setInstallEnabled(false);
return;
}
installItem.setDetails(description.version);
installItem.setIcon(R.drawable.ic_watchface);
installActivity.setInfoText(context.getString(R.string.watchface_install_info, installItem.getName(), description.version, description.author));
LOG.debug("Initialized HuaweiInstallHandler");
} else if (helper.isAPP()) {
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
String screenWindow = String.format("%d*%d", huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().height,
huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().width);
String screenShape = huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().screenShape;
HuaweiAppManager.AppConfig config = helper.getAppconfig();
this.valid = config.checkDistroFilters(screenShape, screenWindow);
installActivity.setInstallEnabled(true);
GenericItem installItem = new GenericItem();
if (helper.getWatchfacePreviewBitmap() != null) {
installItem.setPreview(helper.getWatchfacePreviewBitmap());
}
installItem.setName(config.bundleName);
installActivity.setInstallItem(installItem);
if (device.isBusy()) {
LOG.error("Firmware cannot be installed (device busy)");
installActivity.setInfoText("Firmware cannot be installed (device busy)");
installActivity.setInfoText(device.getBusyTask());
installActivity.setInstallEnabled(false);
return;
}
if (!device.isConnected()) {
LOG.error("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInfoText("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInstallEnabled(false);
return;
}
if (!this.valid) {
LOG.error("App cannot be installed");
installActivity.setInstallEnabled(false);
return;
}
installItem.setDetails(config.version);
installItem.setIcon(R.drawable.ic_watchapp);
installActivity.setInfoText(context.getString(R.string.app_install_info, installItem.getName(), config.version, config.vendor));
LOG.debug("Initialized HuaweiInstallHandler");
}
installItem.setName(description.title);
installActivity.setInstallItem(installItem);
if (device.isBusy()) {
LOG.error("Firmware cannot be installed (device busy)");
installActivity.setInfoText("Firmware cannot be installed (device busy)");
installActivity.setInfoText(device.getBusyTask());
installActivity.setInstallEnabled(false);
return;
}
if ( !device.isConnected()) {
LOG.error("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInfoText("Firmware cannot be installed (not connected or wrong device)");
installActivity.setInstallEnabled(false);
return;
}
if (!this.valid) {
LOG.error("Watchface cannot be installed");
installActivity.setInfoText(context.getString(R.string.watchface_resolution_doesnt_match,
resolution.screenByThemeVersion(description.screen), deviceScreen));
installActivity.setInstallEnabled(false);
return;
}
//installItem.setDetails(description.version);
installItem.setIcon(R.drawable.ic_watchface);
installActivity.setInfoText(context.getString(R.string.watchface_install_info, installItem.getName(), description.version, description.author));
LOG.debug("Initialized HuaweiInstallHandler");
}
@Override

View File

@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Alarms;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.AccountRelated;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Calls;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.CameraRemote;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A;
@ -616,6 +617,16 @@ public class HuaweiPacket {
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
return this;
}
case App.id:
switch (this.commandId) {
case App.AppNames.id:
return new App.AppNames.Response(paramsProvider).fromPacket(this);
case App.AppInfoParams.id:
return new App.AppInfoParams.Response(paramsProvider).fromPacket(this);
default:
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
return this;
}
default:
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
return this;

View File

@ -0,0 +1,139 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
public class App {
public static final byte id = 0x2a;
public static class AppDeviceParams {
public byte unknown1 = 0;
public int unknown2 = 0;
public String osVersion = "";
public String screenShape = "";
public int width = 0;
public int height = 0;
public int unknown3 = 0;
public String buildType = "";
}
public static class InstalledAppInfo {
public String packageName;
public String version;
public int unknown1;
public String appName;
public int unknown2;
public int versionCode;
public byte unknown4;
public byte unknown6;
public InstalledAppInfo(HuaweiTLV tlv) throws HuaweiPacket.MissingTagException {
this.packageName = tlv.getString(0x03);
this.version = tlv.getString(0x04);
this.unknown1 = tlv.getInteger(0x05);
this.appName = tlv.getString(0x06);
this.unknown2 = tlv.getInteger(0x07);
this.versionCode = tlv.getInteger(0x09);
this.unknown4 = tlv.getByte(0x0a);
//{tag: b - Value: } -
this.unknown6 = tlv.getByte(0x0d);
}
}
public static class AppDelete {
public static final byte id = 0x01;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider,
String packageName) {
super(paramsProvider);
this.serviceId = App.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, (byte)1)
.put(0x02, packageName);
}
}
public static class Response extends HuaweiPacket {
public Response (ParamsProvider paramsProvider) {
super(paramsProvider);
}
}
}
public static class AppNames {
public static final byte id = 0x03;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = App.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x81);
}
}
public static class Response extends HuaweiPacket {
public List<App.InstalledAppInfo> appInfoList;
public Response (ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws HuaweiPacket.ParseException {
appInfoList = new ArrayList<>();
if(this.tlv.contains(0x81)) {
for (HuaweiTLV subTlv : this.tlv.getObject(0x81).getObjects(0x82)) {
appInfoList.add(new InstalledAppInfo(subTlv));
}
}
}
}
}
public static class AppInfoParams {
public static final byte id = 0x06;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = App.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x81);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public AppDeviceParams params = new AppDeviceParams();
public Response (ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws ParseException {
if(this.tlv.contains(0x81)) {
HuaweiTLV subTlv = this.tlv.getObject(0x81).getObject(0x82);
this.params.unknown1 = subTlv.getByte(0x03);
this.params.unknown2 = subTlv.getInteger(0x04);
this.params.osVersion = subTlv.getString(0x05);
this.params.screenShape = subTlv.getString(0x06);
this.params.width = subTlv.getShort(0x07);
this.params.height = subTlv.getShort(0x08);
this.params.unknown3 = subTlv.getInteger(0x09);
this.params.buildType = subTlv.getString(0x0a);
}
}
}
}
}

View File

@ -39,7 +39,8 @@ public class FileUpload {
public static final byte watchface = 1;
public static final byte music = 2;
public static final byte backgroundImage = 3;
public static final byte app = 7;
public static final byte app = 6;
public static final byte firmware = 7;
}

View File

@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMes
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Calls;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.CameraRemote;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig;
@ -117,6 +118,7 @@ public class AsynchronousResponse {
handleFileUpload(response);
handleWatchface(response);
handleCameraRemote(response);
handleApp(response);
} catch (Request.ResponseParseException e) {
LOG.error("Response parse exception", e);
}
@ -503,6 +505,22 @@ public class AsynchronousResponse {
}
}
private void handleApp(HuaweiPacket response) throws Request.ResponseParseException {
if (response.serviceId == App.id) {
if (response.commandId == 0x2) {
try {
byte status = response.getTlv().getByte(0x1);
if(status == (byte)0x66 || status == (byte)0x69) {
this.support.getHuaweiAppManager().requestAppList();
}
} catch (HuaweiPacket.MissingTagException e) {
LOG.error("Could not send watchface confirm request", e);
}
}
}
}
private void handleWeatherCheck(HuaweiPacket response) {
if (response.serviceId == Weather.id && response.commandId == 0x04) {
// Send back ok

View File

@ -0,0 +1,162 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAppNames;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendAppDelete;
public class HuaweiAppManager {
static Logger LOG = LoggerFactory.getLogger(HuaweiAppManager.class);
public static class AppConfig {
public String bundleName;
public String vendor;
public String version;
JSONObject distroFilters = null;
public AppConfig(String jsonStr) {
try {
JSONObject config = new JSONObject(jsonStr);
this.bundleName = config.getJSONObject("app").getString("bundleName");
this.vendor = config.getJSONObject("app").getString("vendor");
this.version = config.getJSONObject("app").getJSONObject("version").getString("name");
parseDistroFilters(config);
} catch (Exception e) {
LOG.error("Error decode app config", e);
}
}
private void parseDistroFilters(JSONObject config) {
try {
distroFilters = config.getJSONObject("module").getJSONObject("distroFilter");
} catch (Exception e) {
LOG.error("Error decode app config distroFilter", e);
}
}
private boolean isValInArray(JSONArray arr, String value) throws JSONException {
for (int i = 0; i < arr.length(); i++) {
if (arr.getString(i).equals(value))
return true;
}
return false;
}
public boolean checkDistroFilters(String screenShape, String screenWindow) {
if (distroFilters == null)
return false;
try {
boolean screenShapeSupported = false;
boolean screenWindowSupported = false;
if (distroFilters.has("screenShape")) {
JSONArray values = distroFilters.getJSONObject("screenShape").getJSONArray("value");
String policy = distroFilters.getJSONObject("screenShape").getString("policy");
screenShapeSupported = isValInArray(values, screenShape) && policy.equals("include");
}
if (distroFilters.has("screenWindow")) {
JSONArray values = distroFilters.getJSONObject("screenWindow").getJSONArray("value");
String policy = distroFilters.getJSONObject("screenWindow").getString("policy");
screenWindowSupported = isValInArray(values, screenWindow) && policy.equals("include");
}
return screenShapeSupported && screenWindowSupported;
} catch (Exception e) {
LOG.error("Error decode app config distroFilter", e);
return false;
}
}
}
private final HuaweiSupportProvider support;
private List<App.InstalledAppInfo> installedAppList = null;
public HuaweiAppManager(HuaweiSupportProvider support) {
this.support = support;
}
public void setInstalledAppList(List<App.InstalledAppInfo> installedAppList) {
this.installedAppList = installedAppList;
handleAppList();
}
public void handleAppList() {
if (this.installedAppList == null) {
return;
}
final List<GBDeviceApp> gbDeviceApps = new ArrayList<>();
for (final App.InstalledAppInfo appInfo : installedAppList) {
final UUID uuid = UUID.nameUUIDFromBytes(appInfo.packageName.getBytes());
GBDeviceApp gbDeviceApp = new GBDeviceApp(
uuid,
appInfo.appName,
"",
appInfo.version,
GBDeviceApp.Type.APP_GENERIC
);
gbDeviceApps.add(gbDeviceApp);
}
support.setGbWatchApps(gbDeviceApps);
}
public void requestAppList() {
if (!this.support.getHuaweiCoordinator().supportsAppParams())
return;
try {
GetAppNames getAppNames = new GetAppNames(support);
getAppNames.doPerform();
} catch (IOException e) {
LOG.error("Error request applications list", e);
}
}
public boolean startApp(UUID uuid) {
if (this.installedAppList == null)
return false;
for (final App.InstalledAppInfo appInfo : installedAppList) {
final UUID appUuid = UUID.nameUUIDFromBytes(appInfo.packageName.getBytes());
if (appUuid.equals(uuid))
return true;
}
return false;
}
public boolean deleteApp(UUID uuid) {
if (this.installedAppList == null)
return false;
for (final App.InstalledAppInfo appInfo : installedAppList) {
final UUID appUuid = UUID.nameUUIDFromBytes(appInfo.packageName.getBytes());
if (appUuid.equals(uuid)) {
try {
SendAppDelete sendAppDelete = new SendAppDelete(support, appInfo.packageName);
sendAppDelete.doPerform();
} catch (IOException e) {
LOG.error("Could not delete app: " + appInfo.packageName, e);
}
return true;
}
}
return false;
}
}

View File

@ -25,10 +25,13 @@ import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiAppParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile;
@ -47,6 +50,7 @@ public class HuaweiFwHelper {
Bitmap watchfacePreviewBitmap;
HuaweiWatchfaceManager.WatchfaceDescription watchfaceDescription;
HuaweiAppManager.AppConfig appConfig;
Context mContext;
public HuaweiFwHelper(final Uri uri, final Context context) {
@ -65,13 +69,67 @@ public class HuaweiFwHelper {
}
private void parseFile() {
if (parseAsWatchFace()) {
if(parseAsApp()) {
assert appConfig.bundleName != null;
assert appConfig.distroFilters != null;
fileType = FileUpload.Filetype.app;
} else if (parseAsWatchFace()) {
assert watchfaceDescription.screen != null;
assert watchfaceDescription.title != null;
fileType = FileUpload.Filetype.watchface;
}
}
boolean parseAsApp() {
try {
final UriHelper uriHelper = UriHelper.get(uri, this.mContext);
InputStream inputStream = uriHelper.openInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[4];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] hap_data = buffer.toByteArray();
HuaweiAppParser app = new HuaweiAppParser();
app.parseData(hap_data);
byte[] config = app.getEntryContent("config.json");
if(config == null)
return false;
appConfig = new HuaweiAppManager.AppConfig(new String(config));
fileName = app.getPackageName() + "_INSTALL"; //TODO: INSTALL or UPDATE suffix
fw = hap_data;
fileSize = fw.length;
byte[] icon = app.getEntryContent("icon_small.png");
if(icon != null) {
watchfacePreviewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length);
}
return true;
} catch (FileNotFoundException e) {
LOG.error("The watchface file was not found.", e);
} catch (IOException e) {
LOG.error("General IO error occurred.", e);
} catch (Exception e) {
LOG.error("Unknown error occurred.", e);
}
return false;
}
public byte[] getBytes() {
return fw;
}
@ -137,8 +195,13 @@ public class HuaweiFwHelper {
public boolean isWatchface() {
return fileType == FileUpload.Filetype.watchface;
}
public boolean isAPP() {
return fileType == FileUpload.Filetype.app;
}
public boolean isValid() {
return isWatchface();
return isWatchface() || isAPP();
}
public Bitmap getWatchfacePreviewBitmap() {
@ -149,6 +212,10 @@ public class HuaweiFwHelper {
return watchfaceDescription;
}
public HuaweiAppManager.AppConfig getAppconfig() {
return appConfig;
}
public byte getFileType() {
return fileType;
}

View File

@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
@ -78,6 +79,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
@ -89,6 +91,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AcceptAgreementsRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAppInfoParams;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetEventAlarmList;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetGpsParameterRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationConstraintsRequest;
@ -204,6 +207,8 @@ public class HuaweiSupportProvider {
protected HuaweiFileDownloadManager huaweiFileDownloadManager = new HuaweiFileDownloadManager(this);
protected HuaweiAppManager huaweiAppManager = new HuaweiAppManager(this);
public HuaweiCoordinatorSupplier getCoordinator() {
return ((HuaweiCoordinatorSupplier) this.gbDevice.getDeviceCoordinator());
}
@ -215,6 +220,9 @@ public class HuaweiSupportProvider {
return huaweiWatchfaceManager;
}
public HuaweiAppManager getHuaweiAppManager() {
return huaweiAppManager;
}
public HuaweiSupportProvider(HuaweiBRSupport support) {
this.brSupport = support;
}
@ -802,6 +810,11 @@ public class HuaweiSupportProvider {
SendCameraRemoteSetupEvent sendCameraRemoteSetupEvent = new SendCameraRemoteSetupEvent(this, CameraRemote.CameraRemoteSetup.Request.Event.ENABLE_CAMERA);
sendCameraRemoteSetupEvent.doPerform();
}
if (getHuaweiCoordinator().supportsAppParams()) {
GetAppInfoParams getAppInfoParams = new GetAppInfoParams(this);
getAppInfoParams.doPerform();
}
} catch (IOException e) {
GB.toast(getContext(), "Initialize dynamic services of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR,
e);
@ -2029,19 +2042,50 @@ public class HuaweiSupportProvider {
LOG.error("Failed to update progress notification", e);
}
}
private List<GBDeviceApp> gbWatchFaces = null;
private List<GBDeviceApp> gbWatchApps = null;
public void setGbWatchFaces(List<GBDeviceApp> gbWatchFaces) {
this.gbWatchFaces = gbWatchFaces;
updateAppList();
}
public void setGbWatchApps(List<GBDeviceApp> gbWatchApps) {
this.gbWatchApps = gbWatchApps;
updateAppList();
}
private void updateAppList() {
ArrayList<GBDeviceApp> gbDeviceApps=new ArrayList<>();
if(this.gbWatchFaces != null)
gbDeviceApps.addAll(this.gbWatchFaces);
if(this.gbWatchApps != null)
gbDeviceApps.addAll(this.gbWatchApps);
final GBDeviceEventAppInfo appInfoCmd = new GBDeviceEventAppInfo();
appInfoCmd.apps = gbDeviceApps.toArray(new GBDeviceApp[0]);
evaluateGBDeviceEvent(appInfoCmd);
}
public void onAppInfoReq() {
this.gbWatchFaces = null;
this.gbWatchApps = null;
huaweiWatchfaceManager.requestWatchfaceList();
huaweiAppManager.requestAppList();
}
public void onAppStart(final UUID uuid, boolean start) {
if (start) {
huaweiWatchfaceManager.setWatchface(uuid);
//NOTE: to prevent exception in watchfaces code
if(!huaweiAppManager.startApp(uuid)) {
huaweiWatchfaceManager.setWatchface(uuid);
}
}
}
public void onAppDelete(final UUID uuid) {
huaweiWatchfaceManager.deleteWatchface(uuid);
//NOTE: to prevent exception in watchfaces code
if(!huaweiAppManager.deleteApp(uuid)){
huaweiWatchfaceManager.deleteWatchface(uuid);
}
}
public void onCameraStatusChange(GBDeviceEventCameraRemote.Event event, String filename) {

View File

@ -24,6 +24,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload.FileUploadParams;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -132,7 +133,11 @@ public class HuaweiUploadManager {
public void setDeviceBusy() {
final GBDevice device = support.getDevice();
device.setBusyTask(support.getContext().getString(R.string.uploading_watchface));
if(fileType == FileUpload.Filetype.watchface) {
device.setBusyTask(support.getContext().getString(R.string.uploading_watchface));
} else {
device.setBusyTask(support.getContext().getString(R.string.updating_firmware));
}
device.sendDeviceUpdateIntent(support.getContext());
}

View File

@ -26,20 +26,16 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface.WatchfaceDeviceParams;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfacesList;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfacesNames;
@ -195,10 +191,7 @@ public class HuaweiWatchfaceManager
);
gbDeviceApps.add(gbDeviceApp);
}
final GBDeviceEventAppInfo appInfoCmd = new GBDeviceEventAppInfo();
appInfoCmd.apps = gbDeviceApps.toArray(new GBDeviceApp[0]);
support.evaluateGBDeviceEvent(appInfoCmd);
support.setGbWatchFaces(gbDeviceApps);
}
public void updateWatchfaceNames() {

View File

@ -0,0 +1,35 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class GetAppInfoParams extends Request{
public GetAppInfoParams(HuaweiSupportProvider support) {
super(support);
this.serviceId = App.id;
this.commandId = App.AppInfoParams.id;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new App.AppInfoParams.Request(paramsProvider).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() throws ResponseParseException {
if (!(receivedPacket instanceof App.AppInfoParams.Response))
throw new ResponseTypeMismatchException(receivedPacket, App.AppInfoParams.Response.class);
App.AppInfoParams.Response resp = (App.AppInfoParams.Response)(receivedPacket);
supportProvider.getHuaweiCoordinator().setAppDeviceParams(resp.params);
}
}

View File

@ -0,0 +1,33 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class GetAppNames extends Request{
public GetAppNames(HuaweiSupportProvider support) {
super(support);
this.serviceId = App.id;
this.commandId = App.AppNames.id;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new App.AppNames.Request(paramsProvider).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() throws ResponseParseException {
if (!(receivedPacket instanceof App.AppNames.Response))
throw new ResponseTypeMismatchException(receivedPacket, App.AppNames.Response.class);
App.AppNames.Response resp = (App.AppNames.Response)(receivedPacket);
supportProvider.getHuaweiAppManager().setInstalledAppList(resp.appInfoList);
}
}

View File

@ -0,0 +1,28 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class SendAppDelete extends Request {
private final String packageName;
public SendAppDelete(HuaweiSupportProvider support, String packageName) {
super(support);
this.serviceId = App.id;
this.commandId = App.AppDelete.id;
this.packageName = packageName;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new App.AppDelete.Request(this.paramsProvider, this.packageName).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
}