Gadgetbridge/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js
Daniele Gobbetti 91b1464824 Pebble: use iframe for clay configuration webpages
Replacing the top frame with a data-uri is forbidden since chromium-based-webview version 60.
With this commit we swap the body with an iframe when needed. Old webviews work as usual also with this approach.
No special treatment is needed for local storage as its usage is forbidden in data-uri iframes.

Fixes #758
2017-08-16 21:31:39 +02:00

254 lines
8.1 KiB
JavaScript

var reportedPositionFailures = 0;
navigator.geolocation.getCurrentPosition = function(success, failure, options) { //override because default implementation requires GPS permission
geoposition = JSON.parse(GBjs.getCurrentPosition());
if(options && options.maximumAge && (geoposition.timestamp < Date.now() - options.maximumAge) && reportedPositionFailures <= 10 ) {
reportedPositionFailures++;
failure({ code: 2, message: "POSITION_UNAVAILABLE"});
} else {
reportedPositionFailures = 0;
success(geoposition);
}
}
if (window.Storage){
var prefix = GBjs.getAppLocalstoragePrefix();
GBjs.gbLog("redefining local storage with prefix: " + prefix);
Storage.prototype.setItem = (function(key, value) {
this.call(localStorage,prefix + key, value);
}).bind(Storage.prototype.setItem);
Storage.prototype.getItem = (function(key) {
// console.log("I am about to return " + prefix + key);
var def = null;
if(key == 'clay-settings') {
def = '{}';
}
return this.call(localStorage,prefix + key) || def;
}).bind(Storage.prototype.getItem);
}
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 showStep(desiredStep) {
var steps = document.getElementsByClassName("step");
var testStep = null;
for (var i = 0; i < steps.length; i ++) {
if (steps[i].id == desiredStep)
testStep = steps[i].id;
}
if (testStep !== null) {
for (var i = 0; i < steps.length; i ++) {
steps[i].style.display = 'none';
}
document.getElementById(desiredStep).style.display="block";
}
}
function hideSteps() {
var steps = document.getElementsByClassName("step");
for (var i = 0; i < steps.length; i ++) {
steps[i].style.display = 'none';
}
}
function gbPebble() {
this.configurationURL = null;
this.configurationValues = null;
var self = this;
self.events = {};
//events processing: see http://stackoverflow.com/questions/10978311/implementing-events-in-my-own-object
self.addEventListener = function(name, handler) {
if (self.events.hasOwnProperty(name))
self.events[name].push(handler);
else
self.events[name] = [handler];
}
self.removeEventListener = function(name, handler) {
if (!self.events.hasOwnProperty(name))
return;
var index = self.events[name].indexOf(handler);
if (index != -1)
self.events[name].splice(index, 1);
}
self.evaluate = function(name, args) {
if (!self.events.hasOwnProperty(name))
return;
if (!args || !args.length)
args = [];
var evs = self.events[name], l = evs.length;
for (var i = 0; i < l; i++) {
evs[i].apply(null, args);
}
}
this.actuallyOpenURL = function() {
showStep("step1compat");
window.open(self.configurationURL.toString(), "config");
}
this.actuallySendData = function() {
GBjs.sendAppMessage(self.configurationValues);
GBjs.closeActivity();
}
this.savePreset = function() {
GBjs.saveAppStoredPreset(self.configurationValues);
}
this.loadPreset = function() {
showStep("step2");
var presetElements = document.getElementsByClassName("store_presets");
for (var i = 0; i < presetElements.length; i ++) {
presetElements[i].style.display = 'none';
}
self.configurationValues = GBjs.getAppStoredPreset();
document.getElementById("jsondata").innerHTML=self.configurationValues;
}
//needs to be called like this because of original Pebble function name
this.openURL = function(url) {
if (url.lastIndexOf("http", 0) === 0) {
document.getElementById("config_url").innerHTML=url;
var UUID = GBjs.getAppUUID();
self.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json=");
} else {
//TODO: add custom return_to
var iframe = document.getElementsByTagName('iframe')[0];
var oldbody = document.getElementsByTagName("body")[0];
if (iframe === undefined && oldbody !== undefined) {
iframe = document.createElement("iframe");
oldbody.parentNode.replaceChild(iframe,oldbody);
} else {
hideSteps();
document.documentElement.appendChild(iframe);
}
iframe.src = url;
}
}
this.getActiveWatchInfo = function() {
return JSON.parse(GBjs.getActiveWatchInfo());
}
this.sendAppMessage = function (dict, callbackAck, callbackNack){
try {
self.configurationValues = JSON.stringify(dict);
document.getElementById("jsondata").innerHTML=self.configurationValues;
if (callbackAck != undefined) {
callbackAck();
}
}
catch (e) {
GBjs.gbLog("sendAppMessage failed");
if (callbackNack != undefined) {
callbackNack();
}
}
}
this.getAccountToken = function() {
return '';
}
this.getWatchToken = function() {
return GBjs.getWatchToken();
}
this.getTimelineToken = function() {
return '';
}
this.showSimpleNotificationOnPebble = function(title, body) {
GBjs.gbLog("app wanted to show: " + title + " body: "+ body);
}
this.showConfiguration = function() {
console.error("This watchapp doesn't support configuration");
GBjs.closeActivity();
}
this.parseReturnedPebbleJS = function() {
var str = document.getElementById('pastereturn').value;
var needle = "pebblejs://close#";
if (str.split(needle)[1] !== undefined) {
var t = new Object();
t.response = decodeURIComponent(str.split(needle)[1]);
self.evaluate('webviewclosed',[t]);
showStep("step2");
} else {
console.error("No valid configuration found in the entered string.");
}
}
}
var Pebble = new gbPebble();
var jsConfigFile = GBjs.getAppConfigurationFile();
var storedPreset = GBjs.getAppStoredPreset();
document.addEventListener('DOMContentLoaded', function(){
if (jsConfigFile != null) {
loadScript(jsConfigFile, function() {
Pebble.evaluate('ready', [{'type': "ready"}]); //callback object apparently needed by some watchfaces
if (getURLVariable('config') == 'true') {
showStep("step2");
var json_string = getURLVariable('json');
var t = new Object();
t.response = json_string;
if (json_string != '') {
Pebble.evaluate('webviewclosed',[t]);
}
} else {
if (storedPreset === undefined) {
var presetElements = document.getElementsByClassName("load_presets");
for (var i = 0; i < presetElements.length; i ++) {
presetElements[i].style.display = 'none';
}
}
Pebble.evaluate('showConfiguration');
}
});
}
}, false);