From 089a59168ec0b9757c8a41d12a1087fa1cc1f0ea Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Sun, 28 Feb 2016 22:25:21 +0100 Subject: [PATCH 01/15] Initial support for using an external browser for configuring pebble apps. This allows existing configuration pages to work without having internet access ourselves. This is a better approach as initially thought in #191. What is missing is outlined in the (several) TODOs. --- app/src/main/AndroidManifest.xml | 16 + app/src/main/assets/app_config/configure.html | 25 + app/src/main/assets/app_config/js/Uri.js | 458 ++++++++++++++++++ .../app_config/js/gadgetbridge_boilerplate.js | 95 ++++ .../activities/AppManagerActivity.java | 5 + .../activities/ExternalPebbleJSActivity.java | 153 ++++++ .../devices/pebble/PBWInstallHandler.java | 18 + .../devices/pebble/PBWReader.java | 19 +- .../layout/activity_external_pebble_js.xml | 5 + app/src/main/res/menu/appmanager_context.xml | 4 +- app/src/main/res/values/strings.xml | 1 + 11 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 app/src/main/assets/app_config/configure.html create mode 100644 app/src/main/assets/app_config/js/Uri.js create mode 100644 app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java create mode 100644 app/src/main/res/layout/activity_external_pebble_js.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 49860022e..b9b8c99cb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -257,6 +257,22 @@ android:name="android.appwidget.provider" android:resource="@xml/sleep_alarm_widget_info" /> + + + + + + + + + + + diff --git a/app/src/main/assets/app_config/configure.html b/app/src/main/assets/app_config/configure.html new file mode 100644 index 000000000..ac25d82ce --- /dev/null +++ b/app/src/main/assets/app_config/configure.html @@ -0,0 +1,25 @@ + + + + + + + + + +
+

Url of the configuration:

+
+ +
+
+

Incoming configuration data:

+
+ +
+ diff --git a/app/src/main/assets/app_config/js/Uri.js b/app/src/main/assets/app_config/js/Uri.js new file mode 100644 index 000000000..a3fe49982 --- /dev/null +++ b/app/src/main/assets/app_config/js/Uri.js @@ -0,0 +1,458 @@ +/*! + * jsUri + * https://github.com/derek-watson/jsUri + * + * Copyright 2013, Derek Watson + * Released under the MIT license. + * + * Includes parseUri regular expressions + * http://blog.stevenlevithan.com/archives/parseuri + * Copyright 2007, Steven Levithan + * Released under the MIT license. + */ + + /*globals define, module */ + +(function(global) { + + var re = { + starts_with_slashes: /^\/+/, + ends_with_slashes: /\/+$/, + pluses: /\+/g, + query_separator: /[&;]/, + uri_parser: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*)(?::([^:@\/]*))?)?@)?(\[[0-9a-fA-F:.]+\]|[^:\/?#]*)(?::(\d+|(?=:)))?(:)?)((((?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + }; + + /** + * Define forEach for older js environments + * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility + */ + if (!Array.prototype.forEach) { + Array.prototype.forEach = function(callback, thisArg) { + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + var O = Object(this); + var len = O.length >>> 0; + + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + if (arguments.length > 1) { + T = thisArg; + } + + k = 0; + + while (k < len) { + var kValue; + if (k in O) { + kValue = O[k]; + callback.call(T, kValue, k, O); + } + k++; + } + }; + } + + /** + * unescape a query param value + * @param {string} s encoded value + * @return {string} decoded value + */ + function decode(s) { + if (s) { + s = s.toString().replace(re.pluses, '%20'); + s = decodeURIComponent(s); + } + return s; + } + + /** + * Breaks a uri string down into its individual parts + * @param {string} str uri + * @return {object} parts + */ + function parseUri(str) { + var parser = re.uri_parser; + var parserKeys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "isColonUri", "relative", "path", "directory", "file", "query", "anchor"]; + var m = parser.exec(str || ''); + var parts = {}; + + parserKeys.forEach(function(key, i) { + parts[key] = m[i] || ''; + }); + + return parts; + } + + /** + * Breaks a query string down into an array of key/value pairs + * @param {string} str query + * @return {array} array of arrays (key/value pairs) + */ + function parseQuery(str) { + var i, ps, p, n, k, v, l; + var pairs = []; + + if (typeof(str) === 'undefined' || str === null || str === '') { + return pairs; + } + + if (str.indexOf('?') === 0) { + str = str.substring(1); + } + + ps = str.toString().split(re.query_separator); + + for (i = 0, l = ps.length; i < l; i++) { + p = ps[i]; + n = p.indexOf('='); + + if (n !== 0) { + k = decode(p.substring(0, n)); + v = decode(p.substring(n + 1)); + pairs.push(n === -1 ? [p, null] : [k, v]); + } + + } + return pairs; + } + + /** + * Creates a new Uri object + * @constructor + * @param {string} str + */ + function Uri(str) { + this.uriParts = parseUri(str); + this.queryPairs = parseQuery(this.uriParts.query); + this.hasAuthorityPrefixUserPref = null; + } + + /** + * Define getter/setter methods + */ + ['protocol', 'userInfo', 'host', 'port', 'path', 'anchor'].forEach(function(key) { + Uri.prototype[key] = function(val) { + if (typeof val !== 'undefined') { + this.uriParts[key] = val; + } + return this.uriParts[key]; + }; + }); + + /** + * if there is no protocol, the leading // can be enabled or disabled + * @param {Boolean} val + * @return {Boolean} + */ + Uri.prototype.hasAuthorityPrefix = function(val) { + if (typeof val !== 'undefined') { + this.hasAuthorityPrefixUserPref = val; + } + + if (this.hasAuthorityPrefixUserPref === null) { + return (this.uriParts.source.indexOf('//') !== -1); + } else { + return this.hasAuthorityPrefixUserPref; + } + }; + + Uri.prototype.isColonUri = function (val) { + if (typeof val !== 'undefined') { + this.uriParts.isColonUri = !!val; + } else { + return !!this.uriParts.isColonUri; + } + }; + + /** + * Serializes the internal state of the query pairs + * @param {string} [val] set a new query string + * @return {string} query string + */ + Uri.prototype.query = function(val) { + var s = '', i, param, l; + + if (typeof val !== 'undefined') { + this.queryPairs = parseQuery(val); + } + + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (s.length > 0) { + s += '&'; + } + if (param[1] === null) { + s += param[0]; + } else { + s += param[0]; + s += '='; + if (typeof param[1] !== 'undefined') { + s += encodeURIComponent(param[1]); + } + } + } + return s.length > 0 ? '?' + s : s; + }; + + /** + * returns the first query param value found for the key + * @param {string} key query key + * @return {string} first value found for key + */ + Uri.prototype.getQueryParamValue = function (key) { + var param, i, l; + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (key === param[0]) { + return param[1]; + } + } + }; + + /** + * returns an array of query param values for the key + * @param {string} key query key + * @return {array} array of values + */ + Uri.prototype.getQueryParamValues = function (key) { + var arr = [], i, param, l; + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (key === param[0]) { + arr.push(param[1]); + } + } + return arr; + }; + + /** + * removes query parameters + * @param {string} key remove values for key + * @param {val} [val] remove a specific value, otherwise removes all + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.deleteQueryParam = function (key, val) { + var arr = [], i, param, keyMatchesFilter, valMatchesFilter, l; + + for (i = 0, l = this.queryPairs.length; i < l; i++) { + + param = this.queryPairs[i]; + keyMatchesFilter = decode(param[0]) === decode(key); + valMatchesFilter = param[1] === val; + + if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && (!keyMatchesFilter || !valMatchesFilter))) { + arr.push(param); + } + } + + this.queryPairs = arr; + + return this; + }; + + /** + * adds a query parameter + * @param {string} key add values for key + * @param {string} val value to add + * @param {integer} [index] specific index to add the value at + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.addQueryParam = function (key, val, index) { + if (arguments.length === 3 && index !== -1) { + index = Math.min(index, this.queryPairs.length); + this.queryPairs.splice(index, 0, [key, val]); + } else if (arguments.length > 0) { + this.queryPairs.push([key, val]); + } + return this; + }; + + /** + * test for the existence of a query parameter + * @param {string} key check values for key + * @return {Boolean} true if key exists, otherwise false + */ + Uri.prototype.hasQueryParam = function (key) { + var i, len = this.queryPairs.length; + for (i = 0; i < len; i++) { + if (this.queryPairs[i][0] == key) + return true; + } + return false; + }; + + /** + * replaces query param values + * @param {string} key key to replace value for + * @param {string} newVal new value + * @param {string} [oldVal] replace only one specific value (otherwise replaces all) + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.replaceQueryParam = function (key, newVal, oldVal) { + var index = -1, len = this.queryPairs.length, i, param; + + if (arguments.length === 3) { + for (i = 0; i < len; i++) { + param = this.queryPairs[i]; + if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) { + index = i; + break; + } + } + if (index >= 0) { + this.deleteQueryParam(key, decode(oldVal)).addQueryParam(key, newVal, index); + } + } else { + for (i = 0; i < len; i++) { + param = this.queryPairs[i]; + if (decode(param[0]) === decode(key)) { + index = i; + break; + } + } + this.deleteQueryParam(key); + this.addQueryParam(key, newVal, index); + } + return this; + }; + + /** + * Define fluent setter methods (setProtocol, setHasAuthorityPrefix, etc) + */ + ['protocol', 'hasAuthorityPrefix', 'isColonUri', 'userInfo', 'host', 'port', 'path', 'query', 'anchor'].forEach(function(key) { + var method = 'set' + key.charAt(0).toUpperCase() + key.slice(1); + Uri.prototype[method] = function(val) { + this[key](val); + return this; + }; + }); + + /** + * Scheme name, colon and doubleslash, as required + * @return {string} http:// or possibly just // + */ + Uri.prototype.scheme = function() { + var s = ''; + + if (this.protocol()) { + s += this.protocol(); + if (this.protocol().indexOf(':') !== this.protocol().length - 1) { + s += ':'; + } + s += '//'; + } else { + if (this.hasAuthorityPrefix() && this.host()) { + s += '//'; + } + } + + return s; + }; + + /** + * Same as Mozilla nsIURI.prePath + * @return {string} scheme://user:password@host:port + * @see https://developer.mozilla.org/en/nsIURI + */ + Uri.prototype.origin = function() { + var s = this.scheme(); + + if (this.userInfo() && this.host()) { + s += this.userInfo(); + if (this.userInfo().indexOf('@') !== this.userInfo().length - 1) { + s += '@'; + } + } + + if (this.host()) { + s += this.host(); + if (this.port() || (this.path() && this.path().substr(0, 1).match(/[0-9]/))) { + s += ':' + this.port(); + } + } + + return s; + }; + + /** + * Adds a trailing slash to the path + */ + Uri.prototype.addTrailingSlash = function() { + var path = this.path() || ''; + + if (path.substr(-1) !== '/') { + this.path(path + '/'); + } + + return this; + }; + + /** + * Serializes the internal state of the Uri object + * @return {string} + */ + Uri.prototype.toString = function() { + var path, s = this.origin(); + + if (this.isColonUri()) { + if (this.path()) { + s += ':'+this.path(); + } + } else if (this.path()) { + path = this.path(); + if (!(re.ends_with_slashes.test(s) || re.starts_with_slashes.test(path))) { + s += '/'; + } else { + if (s) { + s.replace(re.ends_with_slashes, '/'); + } + path = path.replace(re.starts_with_slashes, '/'); + } + s += path; + } else { + if (this.host() && (this.query().toString() || this.anchor())) { + s += '/'; + } + } + if (this.query().toString()) { + s += this.query().toString(); + } + + if (this.anchor()) { + if (this.anchor().indexOf('#') !== 0) { + s += '#'; + } + s += this.anchor(); + } + + return s; + }; + + /** + * Clone a Uri object + * @return {Uri} duplicate copy of the Uri + */ + Uri.prototype.clone = function() { + return new Uri(this.toString()); + }; + + /** + * export via AMD or CommonJS, otherwise leak a global + */ + if (typeof define === 'function' && define.amd) { + define(function() { + return Uri; + }); + } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = Uri; + } else { + global.Uri = Uri; + } +}(this)); diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js new file mode 100644 index 000000000..af68a9653 --- /dev/null +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -0,0 +1,95 @@ +function loadScript(url, callback) { + // Adding the script tag to the head as suggested before + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + + // Then bind the event to the callback function. + // There are several events for cross browser compatibility. + script.onreadystatechange = callback; + script.onload = callback; + + // Fire the loading + head.appendChild(script); +} + +function getURLVariable(variable, defaultValue) { + // Find all URL parameters + var query = location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + + // If the query variable parameter is found, decode it to use and return it for use + if (pair[0] === variable) { + return decodeURIComponent(pair[1]); + } + } + return defaultValue || false; +} + +function gbPebble() { + this.configurationURL = null; + this.configurationValues = null; + + this.addEventListener = function(e, f) { + if(e == 'showConfiguration') { + this.showConfiguration = f; + } + if(e == 'webviewclosed') { + this.parseconfig = f; + } + if(e == 'appmessage') { + this.appmessage = f; + } + } + + this.actuallyOpenURL = function() { + window.open(this.configurationURL.toString(), "config"); + } + + this.actuallySendData = function() { + GBjs.sendAppMessage(this.configurationValues); + } + + //needs to be called like this because of original Pebble function name + this.openURL = function(url) { + document.getElementById("config_url").innerHTML=url; + var UUID = GBjs.getAppUUID(); + this.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); + } + + this.getActiveWatchInfo = function() { + return JSON.parse(GBjs.getActiveWatchInfo()); + } + + this.sendAppMessage = function (dict, callback){ + this.configurationValues = JSON.stringify(dict); + document.getElementById("jsondata").innerHTML=this.configurationValues; + return callback; + } + + this.ready = function(e) { + GBjs.gbLog("ready called"); + } +} + +var Pebble = new gbPebble(); + +var jsConfigFile = GBjs.getAppConfigurationFile(); +if (jsConfigFile != null) { + loadScript(jsConfigFile, function() { + if (getURLVariable('config') == 'true') { + document.getElementById('step1').style.display="none"; + var json_string = unescape(getURLVariable('json')); + var t = new Object(); + t.response = json_string; + if (json_string != '') + Pebble.parseconfig(t); + } else { + document.getElementById('step2').style.display="none"; + Pebble.showConfiguration(); + } + }); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index d87f7241a..0ff6333aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -189,6 +189,11 @@ public class AppManagerActivity extends Activity { case R.id.appmanager_health_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; + case R.id.appmanager_app_configure: + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); + startIntent.putExtra("app_uuid", selectedApp.getUUID()); + startActivity(startIntent); + return true; default: return super.onContextItemSelected(item); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java new file mode 100644 index 000000000..cb79cc7a5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -0,0 +1,153 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.util.Pair; +import android.webkit.JavascriptInterface; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class ExternalPebbleJSActivity extends Activity { + + private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); + + //TODO: get device + private Uri uri; + private UUID appUuid; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String queryString = ""; + uri = getIntent().getData(); + if (uri != null) { + //getting back with configuration data + appUuid = UUID.fromString(uri.getHost()); + queryString = uri.getEncodedQuery(); + } else { + appUuid = (UUID) getIntent().getSerializableExtra("app_uuid"); + } + + setContentView(R.layout.activity_external_pebble_js); + getActionBar().setDisplayHomeAsUpEnabled(true); + + WebView myWebView = (WebView) findViewById(R.id.configureWebview); + myWebView.clearCache(true); + WebSettings webSettings = myWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + //needed to access the DOM + webSettings.setDomStorageEnabled(true); + + JSInterface gbJSInterface = new JSInterface(); + myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); + + myWebView.loadUrl("file:///android_asset/app_config/configure.html?"+queryString); + } + + private JSONObject getAppConfigurationKeys() { + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, appUuid.toString() + ".json"); + if(configurationFile.exists()) { + String jsonstring = FileUtils.getStringFromFile(configurationFile); + JSONObject json = new JSONObject(jsonstring); + return json.getJSONObject("appKeys"); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + private class JSInterface { + + public JSInterface () { + } + + @JavascriptInterface + public void gbLog(String msg) { + Log.d("WEBVIEW", msg); + } + + @JavascriptInterface + public void sendAppMessage(String msg) { + Log.d("from WEBVIEW", msg); + JSONObject knownKeys = getAppConfigurationKeys(); + ArrayList> pairs = new ArrayList<>(); + + try{ + JSONObject in = new JSONObject(msg); + String cur_key; + for (Iterator key = in.keys(); key.hasNext();) { + cur_key = key.next(); + int pebbleAppIndex = knownKeys.optInt(cur_key); + if (pebbleAppIndex != 0) { + //TODO: cast to integer (int32) / String? Is it needed? + pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); + } else { + GB.toast("Discarded key "+cur_key+", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + //TODO: send pairs to pebble. (encodeApplicationMessagePush(ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);) + } + + @JavascriptInterface + public String getActiveWatchInfo() { + //TODO: interact with GBDevice, see also todo at the beginning + JSONObject wi = new JSONObject(); + try { + wi.put("platform", "basalt"); + }catch (JSONException e) { + e.printStackTrace(); + } + //Json not supported apparently, we need to cast back and forth + return wi.toString(); + } + + @JavascriptInterface + public String getAppConfigurationFile() { + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, appUuid.toString() + "_config.js"); + if(configurationFile.exists()) { + return "file:///" + configurationFile.getAbsolutePath(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @JavascriptInterface + public String getAppUUID() { + return appUuid.toString(); + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 4071c83df..243b5deb0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -173,6 +173,24 @@ public class PBWInstallHandler implements InstallHandler { } catch (JSONException e) { LOG.error(e.getMessage(), e); } + + String jsConfigFile = mPBWReader.getJsConfigurationFile(); + + if (jsConfigFile != null) { + outputFile = new File(destDir, app.getUUID().toString() + "_config.js"); + try { + writer = new BufferedWriter(new FileWriter(outputFile)); + } catch (IOException e) { + LOG.error("Failed to open output file: " + e.getMessage(), e); + return; + } + try { + writer.write(jsConfigFile); + writer.close(); + } catch (IOException e) { + LOG.error("Failed to write to output file: " + e.getMessage(), e); + } + } } public boolean isValid() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index e463dd443..ee65b5674 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -57,6 +57,7 @@ public class PBWReader { private short mAppVersion; private int mIconId; private int mFlags; + private String jsConfigurationFile = null; private JSONObject mAppKeys = null; @@ -212,6 +213,18 @@ public class PBWReader { e.printStackTrace(); break; } + } else if (fileName.equals("pebble-js-app.js")) { + LOG.info("Found JS file: app supports configuration."); + long bytes = ze.getSize(); + if (bytes > 65536) // that should be too much + break; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((count = zis.read(buffer)) != -1) { + baos.write(buffer, 0, count); + } + + jsConfigurationFile = baos.toString(); } else if (fileName.equals(platformDir + "pebble-app.bin")) { zis.read(buffer, 0, 108); byte[] tmp_buf = new byte[32]; @@ -327,4 +340,8 @@ public class PBWReader { public JSONObject getAppKeysJSON() { return mAppKeys; } -} + + public String getJsConfigurationFile() { + return jsConfigurationFile; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_external_pebble_js.xml b/app/src/main/res/layout/activity_external_pebble_js.xml new file mode 100644 index 000000000..551fb9276 --- /dev/null +++ b/app/src/main/res/layout/activity_external_pebble_js.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index a139eb1a7..dd5d22c3b 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -12,5 +12,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ab859887..fc9e2a29b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,6 +224,7 @@ Deactivate authenticating authentication required + Configure Zzz Add widget From 63d938559ebfae6d5b9f7a072e48a0f73346f51d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 11:52:30 +0100 Subject: [PATCH 02/15] pass GBDevice down to ExternalPebbleJSActivity to determine the platform version (aplite,basalt,chalk) --- .../activities/AppManagerActivity.java | 10 ++++++++++ .../activities/ExternalPebbleJSActivity.java | 13 ++++++++++--- .../devices/pebble/PBWInstallHandler.java | 11 ++--------- .../service/devices/pebble/PebbleIoThread.java | 11 ++--------- .../gadgetbridge/util/PebbleUtils.java | 15 +++++++++++++++ build.gradle | 2 +- 6 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 0ff6333aa..471bc6849 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -30,6 +30,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAppAdapter; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -72,6 +73,7 @@ public class AppManagerActivity extends Activity { private final List appList = new ArrayList<>(); private GBDeviceAppAdapter mGBDeviceAppAdapter; private GBDeviceApp selectedApp = null; + private GBDevice mGBDevice = null; private List getSystemApps() { List systemApps = new ArrayList<>(); @@ -116,6 +118,13 @@ public class AppManagerActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); + } else { + throw new IllegalArgumentException("Must provide a device when invoking this activity"); + } + sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); setContentView(R.layout.activity_appmanager); @@ -192,6 +201,7 @@ public class AppManagerActivity extends Activity { case R.id.appmanager_app_configure: Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); startActivity(startIntent); return true; default: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index cb79cc7a5..9bc3577e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -26,19 +26,27 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class ExternalPebbleJSActivity extends Activity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); - //TODO: get device private Uri uri; private UUID appUuid; + private GBDevice mGBDevice = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); + } else { + throw new IllegalArgumentException("Must provide a device when invoking this activity"); + } + String queryString = ""; uri = getIntent().getData(); if (uri != null) { @@ -119,10 +127,9 @@ public class ExternalPebbleJSActivity extends Activity { @JavascriptInterface public String getActiveWatchInfo() { - //TODO: interact with GBDevice, see also todo at the beginning JSONObject wi = new JSONObject(); try { - wi.put("platform", "basalt"); + wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); }catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 243b5deb0..9794ca8fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -23,6 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class PBWInstallHandler implements InstallHandler { private static final Logger LOG = LoggerFactory.getLogger(PBWInstallHandler.class); @@ -49,15 +50,7 @@ public class PBWInstallHandler implements InstallHandler { return; } - String hwRev = device.getHardwareVersion(); - String platformName; - if (hwRev.startsWith("snowy")) { - platformName = "basalt"; - } else if (hwRev.startsWith("spalding")) { - platformName = "chalk"; - } else { - platformName = "aplite"; - } + String platformName = PebbleUtils.getPlatformName(device.getHardwareVersion()); try { mPBWReader = new PBWReader(mUri, mContext, platformName); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index e3ffaec42..0463cffc4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); @@ -577,15 +578,7 @@ public class PebbleIoThread extends GBDeviceIoThread { return; } - String hwRev = gbDevice.getHardwareVersion(); - String platformName; - if (hwRev.startsWith("snowy")) { - platformName = "basalt"; - } else if (hwRev.startsWith("spalding")) { - platformName = "chalk"; - } else { - platformName = "aplite"; - } + String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); try { mPBWReader = new PBWReader(uri, getContext(), platformName); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java new file mode 100644 index 000000000..b925c3fbd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class PebbleUtils { + public static String getPlatformName(String hwRev) { + String platformName; + if (hwRev.startsWith("snowy")) { + platformName = "basalt"; + } else if (hwRev.startsWith("spalding")) { + platformName = "chalk"; + } else { + platformName = "aplite"; + } + return platformName; + } +} diff --git a/build.gradle b/build.gradle index 6d40ae254..cf58ce202 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.0-beta6' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 860ded102257ae299295634c17c7649f8ef1fb5d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 11:54:07 +0100 Subject: [PATCH 03/15] refromat code --- .../activities/ExternalPebbleJSActivity.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 9bc3577e5..b04dce902 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -70,14 +69,14 @@ public class ExternalPebbleJSActivity extends Activity { JSInterface gbJSInterface = new JSInterface(); myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); - myWebView.loadUrl("file:///android_asset/app_config/configure.html?"+queryString); + myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString); } private JSONObject getAppConfigurationKeys() { try { File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File configurationFile = new File(destDir, appUuid.toString() + ".json"); - if(configurationFile.exists()) { + if (configurationFile.exists()) { String jsonstring = FileUtils.getStringFromFile(configurationFile); JSONObject json = new JSONObject(jsonstring); return json.getJSONObject("appKeys"); @@ -92,7 +91,7 @@ public class ExternalPebbleJSActivity extends Activity { private class JSInterface { - public JSInterface () { + public JSInterface() { } @JavascriptInterface @@ -106,17 +105,17 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject knownKeys = getAppConfigurationKeys(); ArrayList> pairs = new ArrayList<>(); - try{ + try { JSONObject in = new JSONObject(msg); String cur_key; - for (Iterator key = in.keys(); key.hasNext();) { + for (Iterator key = in.keys(); key.hasNext(); ) { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { //TODO: cast to integer (int32) / String? Is it needed? pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); } else { - GB.toast("Discarded key "+cur_key+", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); + GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } } } catch (JSONException e) { @@ -130,7 +129,7 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject wi = new JSONObject(); try { wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); - }catch (JSONException e) { + } catch (JSONException e) { e.printStackTrace(); } //Json not supported apparently, we need to cast back and forth @@ -142,8 +141,8 @@ public class ExternalPebbleJSActivity extends Activity { try { File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File configurationFile = new File(destDir, appUuid.toString() + "_config.js"); - if(configurationFile.exists()) { - return "file:///" + configurationFile.getAbsolutePath(); + if (configurationFile.exists()) { + return "file:///" + configurationFile.getAbsolutePath(); } } catch (IOException e) { e.printStackTrace(); From fa924ff9d8b47f74235da6ee9ef94dc6235c4e4a Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 13:05:51 +0100 Subject: [PATCH 04/15] Pebble: fix crash when navigating back from configuration activity --- app/src/main/AndroidManifest.xml | 1 + .../activities/ExternalPebbleJSActivity.java | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b9b8c99cb..c04cea7ea 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,7 @@ android:label="@string/preferences_miband_settings" android:parentActivityName=".activities.SettingsActivity" /> diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index b04dce902..85b1dcddb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -3,8 +3,10 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.Activity; import android.net.Uri; import android.os.Bundle; +import android.support.v4.app.NavUtils; import android.util.Log; import android.util.Pair; +import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; @@ -31,7 +33,6 @@ public class ExternalPebbleJSActivity extends Activity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); - private Uri uri; private UUID appUuid; private GBDevice mGBDevice = null; @@ -47,7 +48,7 @@ public class ExternalPebbleJSActivity extends Activity { } String queryString = ""; - uri = getIntent().getData(); + Uri uri = getIntent().getData(); if (uri != null) { //getting back with configuration data appUuid = UUID.fromString(uri.getHost()); @@ -81,9 +82,7 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject json = new JSONObject(jsonstring); return json.getJSONObject("appKeys"); } - } catch (IOException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (IOException | JSONException e) { e.printStackTrace(); } return null; @@ -113,7 +112,8 @@ public class ExternalPebbleJSActivity extends Activity { int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { //TODO: cast to integer (int32) / String? Is it needed? - pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); + pairs.add(new Pair<>(pebbleAppIndex, in.get(cur_key))); + LOG.info(in.get(cur_key).getClass().toString()); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } @@ -156,4 +156,15 @@ public class ExternalPebbleJSActivity extends Activity { } } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; + } + return super.onOptionsItemSelected(item); + } + + } From 2a7f9226a0ba1217e0930b0aeae61de5a7369cb5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:23:17 +0100 Subject: [PATCH 05/15] Pebble: send configuration to watch TODO: handle booleans --- .../activities/ExternalPebbleJSActivity.java | 15 ++++++------ .../gadgetbridge/devices/EventHandler.java | 4 ++++ .../gadgetbridge/impl/GBDeviceService.java | 9 +++++++ .../gadgetbridge/model/DeviceService.java | 3 ++- .../service/DeviceCommunicationService.java | 7 ++++++ .../service/ServiceDeviceSupport.java | 8 +++++++ .../service/devices/miband/MiBandSupport.java | 5 ++++ .../service/devices/pebble/PebbleSupport.java | 24 +++++++++++++++++++ 8 files changed, 66 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 85b1dcddb..6f05a0e65 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -5,7 +5,6 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.util.Log; -import android.util.Pair; import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; @@ -19,10 +18,10 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -100,28 +99,28 @@ public class ExternalPebbleJSActivity extends Activity { @JavascriptInterface public void sendAppMessage(String msg) { - Log.d("from WEBVIEW", msg); + LOG.debug("from WEBVIEW: ", msg); JSONObject knownKeys = getAppConfigurationKeys(); - ArrayList> pairs = new ArrayList<>(); try { JSONObject in = new JSONObject(msg); + JSONObject out = new JSONObject(); String cur_key; for (Iterator key = in.keys(); key.hasNext(); ) { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { - //TODO: cast to integer (int32) / String? Is it needed? - pairs.add(new Pair<>(pebbleAppIndex, in.get(cur_key))); - LOG.info(in.get(cur_key).getClass().toString()); + out.put(String.valueOf(pebbleAppIndex), in.get(cur_key)); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } } + LOG.info(out.toString()); + GBApplication.deviceService().onAppConfiguration(appUuid, out.toString()); + } catch (JSONException e) { e.printStackTrace(); } - //TODO: send pairs to pebble. (encodeApplicationMessagePush(ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);) } @JavascriptInterface diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 0d3de3701..8036d8743 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.net.Uri; import android.support.annotation.Nullable; +import android.util.Pair; import java.util.ArrayList; import java.util.UUID; @@ -36,6 +37,8 @@ public interface EventHandler { void onAppDelete(UUID uuid); + void onAppConfiguration(UUID appUuid, String config); + void onFetchActivityData(); void onReboot(); @@ -45,4 +48,5 @@ public interface EventHandler { void onFindDevice(boolean start); void onScreenshotReq(); + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 027a54c3d..9839ade2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.Nullable; +import android.util.Pair; import java.util.ArrayList; import java.util.UUID; @@ -159,6 +160,14 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + Intent intent = createIntent().setAction(ACTION_APP_CONFIGURE) + .putExtra(EXTRA_APP_UUID, uuid) + .putExtra(EXTRA_APP_CONFIG, config); + invokeService(intent); + } + @Override public void onFetchActivityData() { Intent intent = createIntent().setAction(ACTION_FETCH_ACTIVITY_DATA); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index bd4f33e9d..263e174b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -15,7 +15,6 @@ public interface DeviceService extends EventHandler { String ACTION_START = PREFIX + ".action.start"; String ACTION_CONNECT = PREFIX + ".action.connect"; String ACTION_NOTIFICATION = PREFIX + ".action.notification"; - String ACTION_NOTIFICATION_SMS = PREFIX + ".action.notification_sms"; String ACTION_CALLSTATE = PREFIX + ".action.callstate"; String ACTION_SETTIME = PREFIX + ".action.settime"; String ACTION_SETMUSICINFO = PREFIX + ".action.setmusicinfo"; @@ -24,6 +23,7 @@ public interface DeviceService extends EventHandler { String ACTION_REQUEST_SCREENSHOT = PREFIX + ".action.request_screenshot"; String ACTION_STARTAPP = PREFIX + ".action.startapp"; String ACTION_DELETEAPP = PREFIX + ".action.deleteapp"; + String ACTION_APP_CONFIGURE = PREFIX + ".action.app_configure"; String ACTION_INSTALL = PREFIX + ".action.install"; String ACTION_REBOOT = PREFIX + ".action.reboot"; String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test"; @@ -51,6 +51,7 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_TRACK = "music_track"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; + String EXTRA_APP_CONFIG = "app_configt"; String EXTRA_URI = "uri"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index bae6f8b90..f3b8c58b9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -40,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; @@ -60,6 +61,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SE import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_STARTAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALARMS; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; @@ -306,6 +308,11 @@ public class DeviceCommunicationService extends Service { mDeviceSupport.onAppDelete(uuid); break; } + case ACTION_APP_CONFIGURE: { + UUID uuid = (UUID) intent.getSerializableExtra(EXTRA_APP_UUID); + String config = intent.getStringExtra(EXTRA_APP_CONFIG); + mDeviceSupport.onAppConfiguration(uuid, config); + } case ACTION_INSTALL: Uri uri = intent.getParcelableExtra(EXTRA_URI); if (uri != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 4ddeff3af..d13c90d22 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -178,6 +178,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onAppDelete(uuid); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + if (checkBusy("app configuration")) { + return; + } + delegate.onAppConfiguration(uuid, config); + } + @Override public void onFetchActivityData() { if (checkBusy("fetch activity data")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index 63c3c0227..f412e803c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -657,6 +657,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { // not supported } + @Override + public void onAppConfiguration(UUID uuid, String config) { + // not supported + } + @Override public void onScreenshotReq() { // not supported diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java index 0f0b33999..66560c0d0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -1,8 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.net.Uri; +import android.util.Pair; + +import org.json.JSONException; +import org.json.JSONObject; import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; @@ -37,6 +43,24 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { getDeviceIOThread().installApp(uri, 0); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + try { + ArrayList> pairs = new ArrayList<>(); + + JSONObject json = new JSONObject(config); + Iterator keysIterator = json.keys(); + while (keysIterator.hasNext()) { + String keyStr = keysIterator.next(); + Object object = json.get(keyStr); + pairs.add(new Pair<>(Integer.parseInt(keyStr), object)); + } + getDeviceIOThread().write(((PebbleProtocol) getDeviceProtocol()).encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, uuid, pairs)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + @Override public void onHeartRateTest() { From 902ff39c0bd07b7d81d585213e8a9381030d9e61 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:25:44 +0100 Subject: [PATCH 06/15] start app when about to be configured --- .../gadgetbridge/activities/AppManagerActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 471bc6849..38aee186e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -199,6 +199,8 @@ public class AppManagerActivity extends Activity { GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; case R.id.appmanager_app_configure: + GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); From 864e0953d9c42e28bfe925e3c760442309b57583 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:29:46 +0100 Subject: [PATCH 07/15] only allow starting AppManager after device is initalized (else platform cant be determined) --- .../freeyourgadget/gadgetbridge/activities/ControlCenter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 408592c25..e4b04a6be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -131,7 +131,7 @@ public class ControlCenter extends Activity { @Override public void onItemClick(AdapterView parent, View v, int position, long id) { GBDevice gbDevice = deviceList.get(position); - if (gbDevice.isConnected()) { + if (gbDevice.isInitialized()) { DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); Class primaryActivity = coordinator.getPrimaryActivity(); if (primaryActivity != null) { From bd7b34985bfb3bc6ea1863a68568ab5e3084557e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 15:47:00 +0100 Subject: [PATCH 08/15] reformat code and optimize imports --- .../gadgetbridge/activities/AppManagerActivity.java | 2 +- .../freeyourgadget/gadgetbridge/devices/EventHandler.java | 1 - .../freeyourgadget/gadgetbridge/impl/GBDeviceService.java | 1 - .../freeyourgadget/gadgetbridge/model/ActivityUser.java | 2 +- .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 38aee186e..080dba67d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -200,7 +200,7 @@ public class AppManagerActivity extends Activity { return true; case R.id.appmanager_app_configure: GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); - + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 8036d8743..adcf077d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.net.Uri; import android.support.annotation.Nullable; -import android.util.Pair; import java.util.ArrayList; import java.util.UUID; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 9839ade2a..222786937 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.Nullable; -import android.util.Pair; import java.util.ArrayList; import java.util.UUID; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java index cb2b1f489..c0a436a68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -64,7 +64,7 @@ public class ActivityUser { * value is out of any logical bounds. */ public int getActivityUserSleepDuration() { - if(activityUserSleepDuration == null) { + if (activityUserSleepDuration == null) { fetchPreferences(); } if (activityUserSleepDuration < 1 || activityUserSleepDuration > 24) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 0463cffc4..b5bfe222d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -578,7 +578,7 @@ public class PebbleIoThread extends GBDeviceIoThread { return; } - String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); + String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); try { mPBWReader = new PBWReader(uri, getContext(), platformName); From 1e44bb03fb166aa632952c2e78f8d98f10e2f09c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:02:30 +0100 Subject: [PATCH 09/15] Pebble: convert Boolean to String for app configuration --- .../gadgetbridge/activities/ExternalPebbleJSActivity.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 6f05a0e65..61ee95e77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -110,7 +110,11 @@ public class ExternalPebbleJSActivity extends Activity { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { - out.put(String.valueOf(pebbleAppIndex), in.get(cur_key)); + Object obj = in.get(cur_key); + if (obj instanceof Boolean) { + obj = ((Boolean) obj) ? "true" : "false"; + } + out.put(String.valueOf(pebbleAppIndex), obj); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } From 3786e0b7f26f33aa2b258cdd5659adebb4426c67 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:04:17 +0100 Subject: [PATCH 10/15] fix typo --- .../freeyourgadget/gadgetbridge/model/DeviceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index 263e174b5..498cb29c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -51,7 +51,7 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_TRACK = "music_track"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; - String EXTRA_APP_CONFIG = "app_configt"; + String EXTRA_APP_CONFIG = "app_config"; String EXTRA_URI = "uri"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; From a96d042dce1168c3ff771e347fd5830f7915c93d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:07:59 +0100 Subject: [PATCH 11/15] try to make mister travis happy --- .../gadgetbridge/service/TestDeviceSupport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 77158b155..8722f00d0 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -90,6 +90,11 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } + @Override + public void onAppConfiguration(UUID appUuid, String config) { + + } + @Override public void onFetchActivityData() { From aba21d3ab72474d0870ebe01196797d1ca1a31fd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 17:19:29 +0100 Subject: [PATCH 12/15] switch to gradle plugin 1.5.0 in an attempt to fix travis --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cf58ce202..6d40ae254 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-beta6' + classpath 'com.android.tools.build:gradle:1.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From f616e4f571e218870d9fc57f88ec3ae982645351 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 17:46:58 +0100 Subject: [PATCH 13/15] Pebble: skip .js file if too large instead of breaking installation (Hotfix) --- .../gadgetbridge/devices/pebble/PBWReader.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index ee65b5674..e287b3288 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -216,8 +216,10 @@ public class PBWReader { } else if (fileName.equals("pebble-js-app.js")) { LOG.info("Found JS file: app supports configuration."); long bytes = ze.getSize(); - if (bytes > 65536) // that should be too much - break; + if (bytes > 65536) { + LOG.info("size exceeding 64k, skipping"); + continue; + } ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((count = zis.read(buffer)) != -1) { From 3920b3f977895247ea49d3b722b764829c3c85b2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 4 Mar 2016 17:43:43 +0100 Subject: [PATCH 14/15] Do not override the configured settings with our old stored values (but keep them around) --- .../service/devices/pebble/AppMessageHandlerPebStyle.java | 6 ++++++ .../devices/pebble/AppMessageHandlerTimeStylePebble.java | 3 +++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index c29460da9..8b90c5a68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -96,18 +96,24 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); ByteBuffer buf = ByteBuffer.allocate(encodeAck().length + encodePebStyleConfig().length); buf.put(encodeAck()); buf.put(encodePebStyleConfig()); sendBytes.encodedBytes = buf.array(); return new GBDeviceEvent[]{sendBytes}; + */ } @Override public GBDeviceEvent[] pushMessage() { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodePebStyleConfig(); return new GBDeviceEvent[]{sendBytes}; + */ } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 11b8cf78a..fb1583f7e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -100,8 +100,11 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodeTimeStylePebbleConfig(); return new GBDeviceEvent[]{sendBytes}; + */ } } From 6d4b98719a64cab406d0be1fbd44284039b1bf7c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 4 Mar 2016 17:44:42 +0100 Subject: [PATCH 15/15] Implement some further JS methods to make additional watchapps happy --- .../app_config/js/gadgetbridge_boilerplate.js | 45 ++++++++++++++++--- .../activities/ExternalPebbleJSActivity.java | 10 +++++ .../gadgetbridge/util/PebbleUtils.java | 12 +++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index af68a9653..760a2ec83 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -34,6 +34,9 @@ function gbPebble() { this.configurationValues = null; this.addEventListener = function(e, f) { + if(e == 'ready') { + this.ready = f; + } if(e == 'showConfiguration') { this.showConfiguration = f; } @@ -45,6 +48,20 @@ function gbPebble() { } } + this.removeEventListener = function(e, f) { + if(e == 'ready') { + this.ready = null; + } + if(e == 'showConfiguration') { + this.showConfiguration = null; + } + if(e == 'webviewclosed') { + this.parseconfig = null; + } + if(e == 'appmessage') { + this.appmessage = null; + } + } this.actuallyOpenURL = function() { window.open(this.configurationURL.toString(), "config"); } @@ -64,15 +81,31 @@ function gbPebble() { return JSON.parse(GBjs.getActiveWatchInfo()); } - this.sendAppMessage = function (dict, callback){ - this.configurationValues = JSON.stringify(dict); - document.getElementById("jsondata").innerHTML=this.configurationValues; - return callback; + this.sendAppMessage = function (dict, callbackAck, callbackNack){ + try { + this.configurationValues = JSON.stringify(dict); + document.getElementById("jsondata").innerHTML=this.configurationValues; + return callbackAck; + } + catch (e) { + GBjs.gbLog("sendAppMessage failed"); + return callbackNack; + } } - this.ready = function(e) { - GBjs.gbLog("ready called"); + this.getAccountToken = function() { + return ''; } + + this.getWatchToken = function() { + return GBjs.getWatchToken(); + } + + this.showSimpleNotificationOnPebble = function(title, body) { + GBjs.gbLog("app wanted to show: " + title + " body: "+ body); + } + + } var Pebble = new gbPebble(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 61ee95e77..36e8101e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -131,7 +131,11 @@ public class ExternalPebbleJSActivity extends Activity { public String getActiveWatchInfo() { JSONObject wi = new JSONObject(); try { + wi.put("firmware",mGBDevice.getFirmwareVersion()); wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); + wi.put("model", PebbleUtils.getModel(mGBDevice.getHardwareVersion())); + //TODO: use real info + wi.put("language","en"); } catch (JSONException e) { e.printStackTrace(); } @@ -157,6 +161,12 @@ public class ExternalPebbleJSActivity extends Activity { public String getAppUUID() { return appUuid.toString(); } + + @JavascriptInterface + public String getWatchToken() { + //specification says: A string that is is guaranteed to be identical for each Pebble device for the same app across different mobile devices. The token is unique to your app and cannot be used to track Pebble devices across applications. see https://developer.pebble.com/docs/js/Pebble/ + return "gb"+appUuid.toString(); + } } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index b925c3fbd..0e13e925d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -12,4 +12,16 @@ public class PebbleUtils { } return platformName; } + public static String getModel(String hwRev) { + //TODO: get real data? + String model; + if (hwRev.startsWith("snowy")) { + model = "pebble_time_black"; + } else if (hwRev.startsWith("spalding")) { + model = "pebble_time_round_black_20mm"; + } else { + model = "pebble_black"; + } + return model; + } }