encryption.addEventListener('authorized', function() { console.log('Encryption authorized'); // User accepted admin authorization request, so determine whether the device is encrypted var encryptionStatus = encryption.encryptionStatus(); var encrypted = encryptionStatus === encryption.ENCRYPTION_STATUS_ACTIVATED; var encryptionSupported = encryptionStatus === encryption.ENCRYPTION_STATUS_INACTIVE; console.log('Encryption status: '+encryption.encryptionStatus()); console.log('Encrypted: '+encrypted); console.log('Encryption supported: '+encryptionSupported); // Admin privileges are not needed anymore encryption.deauthorizeAdmin(); if (encrypted) { // The device is encrypted, so password protection is optional AD.UI.yesNoAlert($.formatString('passwordProtectOptionalEncrypted', AD.Defaults.application)).then(protectionDfd.resolve, protectionDfd.resolve); } else { // The device is not encrypted if (encryptionSupported) { // Encryption is support to ask the user if they want to encrypt their device first AD.UI.yesNoAlert($.formatString('recommendEncryption', AD.Defaults.application)).then(function() { // Open a webpage with Android full-device encryption instructions Ti.Platform.openURL('http://www.howtogeek.com/141953'); // Now close the application Ti.Android.currentActivity.finish(); }, promptForEncryption); } else { promptForEncryption(); } } });
encryption.addEventListener('authorized', function() { // User accepted admin authorization request, so determine whether the device is encrypted var encrypted = encryption.encryptionStatus() === encryption.ENCRYPTION_STATUS_ACTIVATED; // Admin privileges are not needed anymore encryption.deauthorizeAdmin(); if (encrypted) { // The device is encrypted, so password protection is optional AD.UI.yesNoAlert($.formatString('passwordProtectOptional', AD.Defaults.application)).then(protectionDfd.resolve, protectionDfd.resolve); } else { // The device is not encrypted, so password protection is required AD.UI.okAlert($.formatString('passwordProtectRequired', AD.Defaults.application), true).then(protectionDfd.resolve); } });
module.exports.install = function() { var $ = require('jquery'); var dfd = $.Deferred(); var currentVersion = Ti.App.Properties.getString('version'); if (AD.Platform.isiOS && !currentVersion) { var password = require('com.0x82.key.chain').getPasswordForService('database_key', 'main'); // Only if the password is stored in the 'database_key/main' keychain entry (only pre-1.1) will the PropertyStore be converted from the legacy format if (password) { // In earlier versions (until 1.1), the application version was stored in the encrypted // property store, now it is stored as an unencrypted application property // Open the encrypted PropertyStore using the LEGACY decryptor AD.EncryptionKey.set(password); AD.PropertyStore.read(true); // use the legacy decryptor // Read out the version property, then delete it currentVersion = AD.PropertyStore.get('version'); AD.PropertyStore.remove('version'); // The property store should be flushed because we are changing from the legacy decryptor // to the current decryptor, but it is unnecessary to manually flush the PropertyStore // because the call to AD.PropertyStore.remove(...) already does this //AD.PropertyStore.write(); } } var installed = currentVersion ? true : false; if (!installed) { currentVersion = '0'; } if (compareVersions(AD.Defaults.version, currentVersion) > 0) { // (Re)install the application Ti.API.log((installed ? 'Upgrading' : 'Installing') + ' app from '+currentVersion+' to '+AD.Defaults.version+' ...'); if (installed) { // Updating NextSteps if (AD.Platform.isiOS) { // Starting in 1.1, the password hash is stored as an application property, so calculate // this hash for earlier iOS versions where only the password was stored in the system keychain var keychain = require('com.0x82.key.chain'); var password = keychain.getPasswordForService('database_key', 'main'); if (password) { // This will update the password hash and store the password under "<app-id>/database_encryption_key", // instead of "database_key/main", to prevent conflict between multiple AppDev applications AD.EncryptionKey.set(password); // Remove the now unneeded keychain password entry keychain.deletePasswordForService('database_key', 'main'); } } // The app is already installed, so do not prompt for the random database encryption key dfd.resolve({ updated: true }); } else if (AD.Platform.isiOS) { // Installing on iOS if (AD.Defaults.localStorageEnabled) { // Prompt user for random string var StringPromptWindow = require('ui/StringPromptWindow'); var $winStringPrompt = new StringPromptWindow.EncryptionKey(); $winStringPrompt.getDeferred().done(function(randomString) { // Generate a random key from the random string var key = AD.EncryptionKey.generateKey(randomString); AD.EncryptionKey.set(key); dfd.resolve({ installed: true }); }); } else { dfd.resolve({ installed: true }); } } else { // Installing on Android // Determine whether to password-protect the application var protectionDfd = $.Deferred(); var encryption = require('net.appdevdesigns.encryption'); encryption.addEventListener('authorized', function() { // User accepted admin authorization request, so determine whether the device is encrypted var encrypted = encryption.encryptionStatus() === encryption.ENCRYPTION_STATUS_ACTIVATED; // Admin privileges are not needed anymore encryption.deauthorizeAdmin(); if (encrypted) { // The device is encrypted, so password protection is optional AD.UI.yesNoAlert($.formatString('passwordProtectOptional', AD.Defaults.application)).then(protectionDfd.resolve, protectionDfd.resolve); } else { // The device is not encrypted, so password protection is required AD.UI.okAlert($.formatString('passwordProtectRequired', AD.Defaults.application), true).then(protectionDfd.resolve); } }); encryption.addEventListener('authorizationRejected', function() { // User rejected admin authorization request, so force password protection protectionDfd.resolve(false); }); encryption.authorizeAdmin(); protectionDfd.done(function(passwordProtect) { Ti.API.log(passwordProtect); var StringPromptWindow = require('ui/StringPromptWindow'); var WindowClass = StringPromptWindow[passwordProtect ? 'LoginPassword' : 'EncryptionKey']; $winStringPrompt = new WindowClass(); $winStringPrompt.getDeferred().done(function(password) { if (passwordProtect) { // Use the entered password as the password AD.EncryptionKey.set(password); } else { // Use the entered string to generate a random password, which is saved var key = AD.EncryptionKey.generateKey(password); AD.EncryptionKey.set(key); Ti.App.Properties.setString('password', key); } dfd.resolve({ installed: true }); }); }); } } else { dfd.resolve({}); } dfd.done(function(data) { if (data.installed || data.updated) { if (AD.Defaults.localStorageEnabled) { installDatabases(currentVersion); } Ti.App.Properties.setString('version', AD.Defaults.version); // Set the campus list to an empty array if the property does not exist yet AD.PropertyStore.setDefault('campuses', []); } }); return dfd.promise(); };
module.exports.install = function(hooks) { var dfd = $.Deferred(); var currentVersion = Ti.App.Properties.getString('version'); var password = null; if (AD.Platform.isiOS && !currentVersion) { password = require('com.0x82.key.chain').getPasswordForService('database_key', 'main'); // Only if the password is stored in the 'database_key/main' keychain entry (only pre-1.1) will the PropertyStore be converted from the legacy format if (password) { // In earlier versions (until 1.1), the application version was stored in the encrypted // property store, now it is stored as an unencrypted application property // Open the encrypted PropertyStore using the LEGACY decryptor AD.EncryptionKey.set(password); AD.PropertyStore.read(true); // use the legacy decryptor // Read out the version property, then delete it currentVersion = AD.PropertyStore.get('version'); AD.PropertyStore.remove('version'); // The property store should be flushed because we are changing from the legacy decryptor // to the current decryptor, but it is unnecessary to manually flush the PropertyStore // because the call to AD.PropertyStore.remove(...) already does this //AD.PropertyStore.write(); } } var installed = currentVersion ? true : false; if (!installed) { currentVersion = '0'; } console.log('Installed: '+installed); console.log('Current version: '+currentVersion); console.log('AD.Defaults.version: '+AD.Defaults.version); if (compareVersions(AD.Defaults.version, currentVersion) > 0) { // (Re)install the application console.log((installed ? 'Upgrading' : 'Installing') + ' app from '+currentVersion+' to '+AD.Defaults.version+' ...'); if (installed) { // Updating NextSteps if (AD.Platform.isiOS) { // Starting in 1.1, the password hash is stored as an application property, so calculate // this hash for earlier iOS versions where only the password was stored in the system keychain var keychain = require('com.0x82.key.chain'); password = keychain.getPasswordForService('database_key', 'main'); if (password) { // This will update the password hash and store the password under "<app-id>/database_encryption_key", // instead of "database_key/main", to prevent conflict between multiple AppDev applications AD.EncryptionKey.set(password); // Remove the now unneeded keychain password entry keychain.deletePasswordForService('database_key', 'main'); } } AD.PropertyStore.read(); if (AD.Platform.isAndroid && AD.EncryptionKey.encryptionActivated() && !Ti.App.Properties.getString('password') && !AD.PropertyStore.get('password')) { // The app is encrypted, a password is required, and no PIN is created yet, so prompt the user to choose one AD.Auth.choosePIN().done(function() { dfd.resolve({ updated: true }); }); } else { dfd.resolve({ updated: true }); } } else if (!AD.EncryptionKey.encryptionNecessary()) { // Encryption is unnecessary dfd.resolve({ installed: true }); } else if (AD.Platform.isiOS) { // Installing on iOS AD.Auth.chooseEncryptionKey().done(function() { dfd.resolve({ installed: true }); }); } else { // Installing on Android var promptForEncryption = function() { // Prompt the user regarding application encryption, giving them the opportunity to // opt-out of encryption, if permitted by the application var passwordProtect = function() { protectionDfd.resolve(true); }; if (AD.Defaults.allowUnencrypted) { // Ask the user if they want to encrypt the application, warning them of the consequences of unencryption AD.UI.yesNoAlert($.formatString('passwordProtectOptionalUnencrypted', AD.localize('unencryptedConsequences'))).then(passwordProtect, function() { // Do not encrypt // Do not resolve protectionDfd, thereby never setting up password encryption dfd.resolve({ installed: true }); }); } else { AD.UI.okAlert($.formatString('passwordProtectRequired', AD.Defaults.application)).then(passwordProtect); } }; // Determine whether to password-protect the application var protectionDfd = $.Deferred(); var encryption = require('net.appdevdesigns.encryption'); encryption.addEventListener('authorized', function() { console.log('Encryption authorized'); // User accepted admin authorization request, so determine whether the device is encrypted var encryptionStatus = encryption.encryptionStatus(); var encrypted = encryptionStatus === encryption.ENCRYPTION_STATUS_ACTIVATED; var encryptionSupported = encryptionStatus === encryption.ENCRYPTION_STATUS_INACTIVE; console.log('Encryption status: '+encryption.encryptionStatus()); console.log('Encrypted: '+encrypted); console.log('Encryption supported: '+encryptionSupported); // Admin privileges are not needed anymore encryption.deauthorizeAdmin(); if (encrypted) { // The device is encrypted, so password protection is optional AD.UI.yesNoAlert($.formatString('passwordProtectOptionalEncrypted', AD.Defaults.application)).then(protectionDfd.resolve, protectionDfd.resolve); } else { // The device is not encrypted if (encryptionSupported) { // Encryption is support to ask the user if they want to encrypt their device first AD.UI.yesNoAlert($.formatString('recommendEncryption', AD.Defaults.application)).then(function() { // Open a webpage with Android full-device encryption instructions Ti.Platform.openURL('http://www.howtogeek.com/141953'); // Now close the application Ti.Android.currentActivity.finish(); }, promptForEncryption); } else { promptForEncryption(); } } }); encryption.addEventListener('authorizationRejected', function() { console.log('Encryption rejection'); // User rejected admin authorization request promptForEncryption(); }); encryption.authorizeAdmin(); protectionDfd.done(function(passwordProtect) { console.log('Password protected: '+passwordProtect); if (passwordProtect) { AD.Auth.choosePassword().done(function() { AD.Auth.choosePIN().done(function() { dfd.resolve({ installed: true }); }); }); } else { AD.Auth.chooseEncryptionKey().done(function() { Ti.App.Properties.setString('password', AD.EncryptionKey.get()); dfd.resolve({ installed: true }); }); } }); } } else { dfd.resolve({}); } dfd.done(function(data) { if (data.installed || data.updated) { var DataStore = require('appdev/db/DataStoreSQLite'); // This is the data that is passed to hooks var installData = { labels: null, previousVersion: currentVersion, // the app version before the upgrade currentVersion: AD.Defaults.version, // the app version after the upgrade dbName: AD.Defaults.dbName, query: function(query, values) { // Run a database query return DataStore.execute(installData.dbName, query, values); }, installLabels: function(tableName) { var table = installData.labels[tableName]; var dataTableName = tableName+'_data'; var transTableName = tableName+'_trans'; var getDataMgr = function(dbTable, model) { return { dbName: installData.dbName, dbTable: dbTable, model: $.extend(table.has_uuid ? { viewer_id: AD.Defaults.viewerId, device_id: Ti.Platform.id } : {}, model), joinedTables: [], selectedFields: { _empty: true } }; }; table.labels.forEach(function(label) { // Create the data table entry DataStore.create(getDataMgr(dataTableName)).done(function(primaryKey) { // Get the value of the linked field from the new entry var getLinkedFieldDfd = $.Deferred(); if (table.has_uuid) { DataStore.read(getDataMgr(dataTableName, $.makeObject([{ key: table.primary_key, value: primaryKey }]))).done(function(labelArgs) { getLinkedFieldDfd.resolve(labelArgs[0][0][table.linked_field]); }); } else { getLinkedFieldDfd.resolve(primaryKey); } // Create the trans table entries for each label getLinkedFieldDfd.done(function(linkedField) { $.each(label, function(language, label) { DataStore.create(getDataMgr(transTableName, $.makeObject([ { key: table.linked_field, value: linkedField }, { key: 'language_code', value: language }, { key: table.label_field, value: label } ]))); }); }); }); }); } }; // Read in the labels data file var labelsFile = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'labels.json'); if (labelsFile.exists) { installData.labels = JSON.parse(labelsFile.read().text); } // Load the property store so that it will be accessible to the installer hooks AD.PropertyStore.read(); if (AD.Defaults.localStorageEnabled) { if (data.installed) { installDatabases(installData); if (hooks && $.isFunction(hooks.installDatabases)) { hooks.installDatabases(installData); } } else if (data.updated) { upgradeDatabases(installData); if (hooks && $.isFunction(hooks.upgradeDatabases)) { hooks.upgradeDatabases(installData); } } } // This property was removed after version 1.1 AD.PropertyStore.remove('viewer'); Ti.App.Properties.setString('version', AD.Defaults.version); if (hooks && $.isFunction(hooks.onInstall)) { hooks.onInstall(installData); } if (compareVersions(currentVersion, '0') > 0 && compareVersions(currentVersion, '1.5') < 0) { // Rename the file ServiceJSON.retryingRequests.json to HTTP.retryingRequests.json var file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'ServiceJSON.retryingRequests.json'); file.rename('HTTP.retryingRequests.json'); } } }); return dfd.promise(); };