@titanium/updater
Version:
⭐ Axway Amplify module to check for app updates with Appcelerator Titanium SDK Framework
313 lines (247 loc) • 10.1 kB
JavaScript
/* eslint-disable no-async-promise-executor */
/* eslint-disable promise/avoid-new */
const logger = require('@geek/logger').createLogger('@titanium/updater', { meta: { filename: __filename } });
logger.track(`📦 you are here → entering @titanium/updater`);
// TIBUG: Need to define these as Titanium isn't parsing things in node_modules properly
const OS_IOS = Titanium.App.iOS;
const OS_ANDROID = !Titanium.App.iOS;
const moment = require('moment');
const semver = require('./semver');
// const turbo = require('/turbo');
// logger.debug(`turbo: ${JSON.stringify(turbo, null, 2)}`);
const Please = require('@titanium/please');
class Updater {
constructor({
url,
timeout = 60000,
id = turbo.app_id,
platform = turbo.os_name_full,
version = turbo.app_version,
baseUrl,
channel = 'release',
// message = `We've delivered a shiny, new version of the app but we need you need to update the app in order to continue.`,
} = {}) {
logger.track('📦 you are here → @titanium/updater.constructor()');
if (!url && !baseUrl) {
throw new Error('@titanium/updater requires either baseUrl or url');
}
this.url = url;
this.baseUrl = baseUrl;
this.id = id;
this.platform = platform;
this.version = version;
this.timeout = timeout;
this.platform_lower = turbo.os_name;
this.channel = channel;
if (!url) {
this.url = `${this.baseUrl}/${this.id}/${this.platform_lower}/app-channel-${this.channel}.json`;
if (this.channel !== 'release') {
this.url_fallback = `${this.baseUrl}/${this.id}/${this.platform_lower}/app-channel-release.json`;
}
}
this.appInfoPlease = new Please({
baseUrl: this.url,
timeout,
headers: {
'Cache-Control': 'no-cache',
'Accept-Encoding': 'identity',
},
});
// this.message = message;
}
async ensure({ recommended = true, optional = false, openHomePage = false } = {}) {
logger.track('📦 you are here → @titanium/updater.ensure');
logger.debug(`📦 recommended: ${JSON.stringify(recommended, null, 2)}`);
logger.debug(`📦 optional: ${JSON.stringify(optional, null, 2)}`);
logger.debug(`📦 openHomePage: ${JSON.stringify(openHomePage, null, 2)}`);
return new Promise(async (resolve, reject) => {
let result;
try {
result = await this.appInfoPlease
.debug(turbo.API_VERBOSE_MODE)
.get();
} catch (error) {
logger.track(`📦 you are here → updater.appInfoPlease.catch`);
logger.debug(`⛔ error: ${JSON.stringify(error, null, 2)}`);
if (this.url_fallback) {
result = await this.appInfoPlease
.url(this.url_fallback)
.debug(turbo.API_VERBOSE_MODE)
.get();
} else {
throw error;
}
}
logger.track('📦 you are here → @titanium/updater.ensure.then()');
const appInfo = result.json || {};
logger.verbose(`📦 appInfo: ${JSON.stringify(appInfo, null, 2)}`);
if (!appInfo.latest) {
console.warn(`no app version info found for app: ${this.id} platform: ${this.platform}`);
return resolve(true);
}
const meetsRequired = semver.satisfies(semver.coerce(this.version), appInfo.required);
const meetsRecommended = semver.satisfies(semver.coerce(this.version), appInfo.recommended);
const meetsOptional = semver.gte(semver.coerce(this.version), appInfo.latest);
logger.debug(`📦 currentVersion: ${JSON.stringify(this.version, null, 2)}`);
logger.debug(`📦 latestVersion: ${JSON.stringify(appInfo.latest, null, 2)}`);
logger.debug(`📦 meetsRequired: ${JSON.stringify(meetsRequired, null, 2)}`);
logger.debug(`📦 meetsRecommended: ${JSON.stringify(meetsRecommended, null, 2)}`);
logger.debug(`📦 meetsOptional: ${JSON.stringify(meetsOptional, null, 2)}`);
if (meetsRequired) {
logger.info(`📦 App version ${this.version} meets requirements of: ${appInfo.required}`);
if (recommended && meetsRecommended) {
logger.info(`📦 App version ${this.version} meets recommendations of: ${appInfo.recommended}`);
if (optional && meetsOptional) {
logger.info(`📦 App version ${this.version} meets optional updates of: >=${appInfo.latest}`);
return resolve(true);
} else if (! optional) {
return resolve(true);
}
} else if (! recommended) {
return resolve(true);
}
}
const release = _.find(appInfo.releases, { version: appInfo.latest });
const handleUpdateEvent = async (e, args) => {
logger.track(`📦 you are here → @titanium/updater handling event - updater::update`);
// Open homepage as possible workaround for possible Android issues
if (openHomePage) {
const alertNotice = Ti.UI.createAlertDialog({
cancel: 1,
title: 'Updated Version',
message: 'This will open the web page where you can download and install the latest version of the app.',
buttonNames: [ 'Continue', 'Cancel' ],
});
alertNotice.addEventListener('click', async event => {
if (event.index === event.source.cancel) {
logger.track(`📌 you are here → updater: update cancelled`);
if (meetsRequired) {
Alloy.close('update-required');
return resolve();
}
} else {
Ti.Platform.openURL(appInfo.homepage);
if (meetsRequired) {
Alloy.close('update-required');
return resolve();
}
}
});
logger.track(`📦 you are here → alertNotice.show()`);
alertNotice.show();
return;
}
turbo.events.off('updater::update', handleUpdateEvent);
const install_url = release['install-url'];
logger.debug(`📦 install_url: ${JSON.stringify(install_url, null, 2)}`);
Alloy.close('update-required');
turbo.openLoadingScreen();
if (OS_IOS) {
Titanium.Platform.openURL(install_url, {}, e => {
logger.track(`📦 you are here → @titanium/updater updater::update openURL handler`);
});
} else {
// Titanium.Platform.openURL(install_url, {}, e => {
// logger.track(`📦 you are here → @titanium/updater updater::update openURL handler`);
// turbo.openLoadingScreen();
// });
try {
const apk = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'update.apk');
const apkPlease = new Please({
baseUrl: install_url,
timeout: this.timeout,
headers: { 'Cache-Control': 'no-cache' },
file: apk,
});
await apkPlease.get();
const alertNotice = Ti.UI.createAlertDialog({
cancel: 1,
title: 'Update downloaded',
message: 'Please follow prompting to install the update.\n\nNOTE: You will have to manually relaunch the app after the update.',
buttonNames: [ 'Continue', 'Cancel' ],
});
alertNotice.addEventListener('click', async event => {
if (event.index === event.source.cancel) {
logger.track(`📦 you are here → updater: update cancelled`);
alertNotice.hide();
// await Promise.delay(1000);
// Ti.Platform.openURL(appInfo.homepage);
turbo.events.fire('updater::ignore');
resolve();
} else {
turbo.events.off(`updater::ignore`, handleIgnoreEvent);
install_android_apk();
}
});
// Ti.Analytics.featureEvent('update:fail');
// Alloy.Globals.ACA.logHandledException(e);
logger.track(`📦 you are here → alertNotice.show()`);
alertNotice.show();
const install_android_apk = () => {
logger.track(`📦 you are here → updater.install_android_apk()`);
setTimeout(() => {
let intent = Ti.Android.createIntent({});
// intent.putExtraUri('uri', apk.nativePath);
logger.debug(`📦 apk.nativePath: ${JSON.stringify(apk.nativePath, null, 2)}`);
intent = Ti.Android.createIntent({
action: 'android.intent.action.INSTALL_PACKAGE',
// data: intent.getStringExtra('uri'),
data: apk.nativePath,
flags: Ti.Android.FLAG_GRANT_READ_URI_PERMISSION,
type: 'application/vnd.android.package-archive',
});
intent.putExtra('EXTRA_NOT_UNKNOWN_SOURCE', true);
Ti.Android.currentActivity.startActivity(intent);
}, 10);
};
} catch (error) {
logger.error(error);
const alertNotice = Ti.UI.createAlertDialog({
title: 'Auto-update failed',
message: 'Auto-update failed. Please manually download and update via your browser',
buttonNames: [ 'Download' ],
});
alertNotice.addEventListener('click', async event => {
if (event.index === 0) {
Ti.Platform.openURL(appInfo.homepage);
}
alertNotice.hide();
});
Ti.Analytics.featureEvent('update:fail');
Alloy.Globals.ACA.logHandledException(e);
alertNotice.show();
}
}
// Alloy.close('update-required');
// return resolve();
};
const handleIgnoreEvent = async (e, args) => {
logger.track(`📦 you are here → @titanium/updater handling event - updater::ignore`);
turbo.events.off(`updater::ignore`, handleIgnoreEvent);
turbo.events.off('updater::update', handleUpdateEvent);
Alloy.close('update-required');
turbo.closeLoadingScreen();
return resolve();
};
turbo.events.on('updater::ignore', handleIgnoreEvent);
turbo.events.on('updater::update', handleUpdateEvent);
Alloy.open('update-required', { optional: meetsRequired, message: this.message, version: release.version, channel: this.channel });
})
.catch(error => {
logger.error('⛔ → Error: Error occurred in @titanium/updater.ensure()');
logger.error(error);
logger.error(`error: ${JSON.stringify(error, null, 2)}`);
// resolve();
// reject(error);
});
}
update() {
logger.track(`📦 you are here → @titanium/updater.update()`);
turbo.events.fire(`updater::update`);
}
ignore() {
logger.track(`📦 you are here → @titanium/updater.ignore()`);
turbo.events.fire(`updater::ignore`);
}
}
module.exports = Updater;