├── screenshot.png ├── DefaultIcon.png ├── screenshots.png ├── app ├── assets │ ├── android │ │ ├── appicon.png │ │ └── images │ │ │ ├── res-hdpi │ │ │ └── ic_action_action_autorenew.png │ │ │ ├── res-mdpi │ │ │ └── ic_action_action_autorenew.png │ │ │ ├── res-xhdpi │ │ │ └── ic_action_action_autorenew.png │ │ │ └── res-xxhdpi │ │ │ └── ic_action_action_autorenew.png │ └── feed.xml ├── models │ └── feed.js ├── platform │ └── android │ │ └── res │ │ ├── drawable-hdpi │ │ ├── appicon.png │ │ └── background.9.png │ │ ├── drawable-mdpi │ │ ├── appicon.png │ │ └── background.9.png │ │ ├── drawable-xhdpi │ │ ├── appicon.png │ │ └── background.9.png │ │ ├── drawable-xxhdpi │ │ ├── appicon.png │ │ └── background.9.png │ │ └── values │ │ └── custom_theme.xml ├── styles │ ├── app.tss │ └── master.tss ├── config.json ├── views │ ├── detail.xml │ ├── index.xml │ └── master.xml ├── alloy.js ├── alloy.jmk ├── controllers │ ├── detail.js │ ├── index.js │ └── master.js └── lib │ └── alloy │ └── sync │ └── rss.js ├── package.json ├── .gitignore ├── manifest ├── plugins ├── ti.version │ └── 1.0 │ │ └── hooks │ │ └── version.js └── ti.alloy │ ├── hooks │ ├── deepclean.js │ └── alloy.js │ └── plugin.py ├── .project ├── tiapp.xml ├── README.md └── LICENSE /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/screenshot.png -------------------------------------------------------------------------------- /DefaultIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/DefaultIcon.png -------------------------------------------------------------------------------- /screenshots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/screenshots.png -------------------------------------------------------------------------------- /app/assets/android/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/assets/android/appicon.png -------------------------------------------------------------------------------- /app/models/feed.js: -------------------------------------------------------------------------------- 1 | exports.definition = { 2 | config: { 3 | adapter: { 4 | type: 'rss', 5 | idAttribute: 'guid' 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "assets": "ticons icons _assets/icon_shape.png && ticons icons _assets/icon.png -p ios && ticons splashes _assets/icon.png -c" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Resources 3 | build.log 4 | build 5 | npm-debug.log 6 | tmp 7 | .map 8 | .project 9 | .settings 10 | Thumbs.db 11 | _assets 12 | /i18n 13 | /platform 14 | -------------------------------------------------------------------------------- /app/platform/android/res/drawable-hdpi/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-hdpi/appicon.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-mdpi/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-mdpi/appicon.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-xhdpi/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-xhdpi/appicon.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-xxhdpi/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-xxhdpi/appicon.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-hdpi/background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-hdpi/background.9.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-mdpi/background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-mdpi/background.9.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-xhdpi/background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-xhdpi/background.9.png -------------------------------------------------------------------------------- /app/platform/android/res/drawable-xxhdpi/background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/platform/android/res/drawable-xxhdpi/background.9.png -------------------------------------------------------------------------------- /app/assets/android/images/res-hdpi/ic_action_action_autorenew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/assets/android/images/res-hdpi/ic_action_action_autorenew.png -------------------------------------------------------------------------------- /app/assets/android/images/res-mdpi/ic_action_action_autorenew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/assets/android/images/res-mdpi/ic_action_action_autorenew.png -------------------------------------------------------------------------------- /app/assets/android/images/res-xhdpi/ic_action_action_autorenew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/assets/android/images/res-xhdpi/ic_action_action_autorenew.png -------------------------------------------------------------------------------- /app/assets/android/images/res-xxhdpi/ic_action_action_autorenew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-developer-relations/appc-sample-rss/HEAD/app/assets/android/images/res-xxhdpi/ic_action_action_autorenew.png -------------------------------------------------------------------------------- /manifest: -------------------------------------------------------------------------------- 1 | #appname:Sample.RSS 2 | #publisher:Axway 3 | #url:https://www.axway.com 4 | #image:appicon.png 5 | #appid:com.appcelerator.sample.rss 6 | #desc:Sample RSS Reader app created with Titanium 7 | #type:ipad 8 | -------------------------------------------------------------------------------- /app/styles/app.tss: -------------------------------------------------------------------------------- 1 | 'Window': { 2 | backgroundColor: '#FFF' 3 | } 4 | 5 | 'Window[platform=ios]': { 6 | barColor: '#CD1625', 7 | navTintColor: '#FFF', 8 | translucent: false, 9 | titleAttributes: { 10 | color: '#FFF' 11 | } 12 | } 13 | 14 | 'Label': { 15 | color: '#000' 16 | } -------------------------------------------------------------------------------- /app/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "url": "https://devblog.axway.com/feed" 4 | }, 5 | "env:development": {}, 6 | "env:test": {}, 7 | "env:production": {}, 8 | "os:android": {}, 9 | "os:blackberry": {}, 10 | "os:ios": {}, 11 | "os:mobileweb": {}, 12 | "dependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /app/views/detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/styles/master.tss: -------------------------------------------------------------------------------- 1 | '.item': { 2 | height: 60 3 | } 4 | 5 | '.text': { 6 | left: 10, 7 | height: 25, 8 | ellipsize: Titanium.UI.TEXT_ELLIPSIZE_TRUNCATE_MARQUEE 9 | } 10 | 11 | '.title': { 12 | top: 5, 13 | 14 | font: { 15 | fontSize: 18 16 | } 17 | } 18 | 19 | '.subtitle': { 20 | top: 30, 21 | 22 | font: { 23 | fontSize: 18 24 | }, 25 | color: '#999' 26 | } -------------------------------------------------------------------------------- /app/platform/android/res/values/custom_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /plugins/ti.version/1.0/hooks/version.js: -------------------------------------------------------------------------------- 1 | exports.init = function(logger, config, cli, appc) { 2 | if (cli.tiapp.properties['ti.version.range']) { 3 | if (!appc.version.satisfies(cli.sdk.manifest.name, cli.tiapp.properties['ti.version.range'].value)) { 4 | logger.error('This app requires Titanium SDK ' + cli.tiapp.properties['ti.version.range'].value + ' instead of ' + cli.sdk.name + ' (' + cli.sdk.manifest.name + ')'); 5 | process.exit(1); 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /app/alloy.js: -------------------------------------------------------------------------------- 1 | // The contents of this file will be executed before any of 2 | // your view controllers are ever executed, including the index. 3 | // You have access to all functionality on the `Alloy` namespace. 4 | // 5 | // This is a great place to do any initialization for your app 6 | // or create any global variables/functions that you'd like to 7 | // make available throughout your app. You can easily make things 8 | // accessible globally by attaching them to the `Alloy.Globals` 9 | // object. For example: 10 | // 11 | // Alloy.Globals.someGlobalFunction = function(){}; 12 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | RSS Reader 4 | 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | com.appcelerator.titanium.core.builder 15 | 16 | 17 | 18 | 19 | 20 | com.appcelerator.titanium.mobile.nature 21 | com.appcelerator.titanium.alloy.core.nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/alloy.jmk: -------------------------------------------------------------------------------- 1 | task('pre:load', function(event, logger) { 2 | 3 | if (event.minVersion) { 4 | var pkg = require(require('path').join(process.mainModule.filename, '..', '..', 'package.json')); 5 | 6 | if (versionStringToInt(pkg.version) < versionStringToInt(event.minVersion)) { 7 | logger.error('This app requires Alloy ' + event.minVersion + ' or later instead of ' + pkg.version); 8 | process.exit(1); 9 | } 10 | } 11 | }); 12 | 13 | function versionStringToInt(versionStr) { 14 | return versionStr.split(/[^0-9]+/).reverse().reduce(function(previousValue, currentValue, currentIndex) { 15 | return previousValue + Math.pow(100, currentIndex) * parseInt(currentValue, 10); 16 | }, 0); 17 | } 18 | -------------------------------------------------------------------------------- /app/controllers/detail.js: -------------------------------------------------------------------------------- 1 | /** 2 | * self-executing function to organize otherwise inline constructor code 3 | * @param {Object} args arguments passed to the controller 4 | */ 5 | (function constructor(args) { 6 | 7 | // use strict mode for this function scope 8 | 'use strict'; 9 | 10 | // model passed to the controller 11 | var model = args.model; 12 | 13 | // set the Window title and WebView URL 14 | $.win.title = model.get('title'); 15 | $.webView.url = model.get('link'); 16 | 17 | // execute constructor with optional arguments passed to controller 18 | })(arguments[0] || {}); 19 | 20 | /** 21 | * Event listener set via view to be called on when the user taps the home-icon (Android) 22 | */ 23 | function close() { 24 | 'use strict'; 25 | 26 | // close the window, showing the master window behind it 27 | $.win.close(); 28 | } 29 | -------------------------------------------------------------------------------- /app/views/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/views/master.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/controllers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * self-executing function to organize otherwise inline constructor code 3 | * @param {Object} args arguments passed to the controller 4 | */ 5 | (function constructor(args) { 6 | 7 | // use strict mode for this function scope 8 | 'use strict'; 9 | 10 | if (OS_IOS) { 11 | 12 | // open SplitWindow for iPad 13 | if (Alloy.isTablet) { 14 | $.splitWin.open(); 15 | 16 | // open NavigationWindow for iPhone 17 | } else { 18 | $.navWin.open(); 19 | } 20 | 21 | // open NavigationGroup's wrapper Window for MobileWeb 22 | } else if (OS_MOBILEWEB) { 23 | $.win.open(); 24 | 25 | // open master controller's Window view for Android (and other platforms) 26 | } else { 27 | $.masterCtrl.getView().open(); 28 | } 29 | 30 | // execute constructor with optional arguments passed to controller 31 | })(arguments[0] || {}); 32 | 33 | /** 34 | * event listener set via view for the select-event of the master controller 35 | * @param {Object} e Event 36 | */ 37 | function onSelect(e) { 38 | 'use strict'; 39 | 40 | // selected model passed with the event 41 | var model = e.model; 42 | 43 | // create the detail controller with the model and get its view 44 | var win = Alloy.createController('detail', { 45 | model: model 46 | }).getView(); 47 | 48 | // open the window in the NavigationWindow for iOS 49 | if (OS_IOS) { 50 | $.navWin.openWindow(win); 51 | 52 | // open the window in the NavigationGroup for MobileWeb 53 | } else if (OS_MOBILEWEB) { 54 | $.navWin.open(win); 55 | 56 | // simply open the window on top for Android (and other platforms) 57 | } else { 58 | win.open(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /plugins/ti.alloy/hooks/deepclean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Alloy 3 | * Copyright (c) 2014 by Appcelerator, Inc. All Rights Reserved. 4 | * See LICENSE for more information on licensing. 5 | */ 6 | 7 | exports.cliVersion = '>=3.X'; 8 | var SILENT = true; 9 | 10 | exports.init = function (logger, config, cli, appc) { 11 | var path = require('path'), 12 | fs = require('fs'), 13 | afs = appc.fs; 14 | 15 | function run(finished) { 16 | if (cli.argv['shallow'] === '') { 17 | logger.info('Not cleaning the Resources directory'); 18 | finished(); 19 | return; 20 | } 21 | var appDir = path.join(cli.argv['project-dir'], 'app'); 22 | if (!afs.exists(appDir)) { 23 | logger.debug('Project not an Alloy app, exiting.'); 24 | finished(); 25 | return; 26 | } 27 | 28 | var resourcesDir = path.join(cli.argv['project-dir'], 'Resources'); 29 | if (!afs.exists(resourcesDir)) { 30 | logger.debug('Resources directory does not exist.'); 31 | finished(); 32 | return; 33 | } 34 | rmdir(resourcesDir, fs, path, logger); 35 | logger.debug('Resources directory of %s has been emptied', appDir.cyan); 36 | finished(); 37 | } 38 | 39 | cli.addHook('clean.post', function (build, finished) { 40 | run(finished); 41 | }); 42 | 43 | }; 44 | 45 | function rmdir(dirPath, fs, path, logger, removeSelf) { 46 | var files; 47 | try { 48 | files = fs.readdirSync(dirPath); 49 | } catch (e) { 50 | return; 51 | } 52 | if (files.length > 0) { 53 | for (var i = 0; i < files.length; i++) { 54 | var filePath = path.join(dirPath, files[i]); 55 | if (fs.statSync(filePath).isFile()) { 56 | fs.unlinkSync(filePath); 57 | } else { 58 | rmdir(filePath, fs, path, logger, true); 59 | } 60 | } 61 | } 62 | if (removeSelf) { 63 | fs.rmdirSync(dirPath); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tiapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.appcelerator.sample.rss 4 | RSS Reader 5 | 2.0.1 6 | Axway 7 | https://www.axway.com 8 | Sample RSS Reader app created with Titanium 9 | 2019 by Appcelerator, Inc. 10 | appicon.png 11 | false 12 | false 13 | false 14 | 11111111-1111-1111-1111-111111111111 15 | dp 16 | >=5.2.2 17 | 18 | true 19 | true 20 | #C6162D 21 | 22 | 23 | UISupportedInterfaceOrientations~iphone 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationLandscapeLeft 27 | UIInterfaceOrientationLandscapeRight 28 | 29 | UISupportedInterfaceOrientations~ipad 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationPortraitUpsideDown 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | 36 | UIRequiresPersistentWiFi 37 | 38 | UIPrerenderedIcon 39 | 40 | UIStatusBarHidden 41 | 42 | UIStatusBarStyle 43 | UIStatusBarStyleLightContent 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | true 55 | true 56 | true 57 | true 58 | 59 | 8.2.0.GA 60 | 61 | ti.alloy 62 | ti.version 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/controllers/master.js: -------------------------------------------------------------------------------- 1 | // require the built-in MomentJS library 2 | var moment = require('alloy/moment'); 3 | 4 | /** 5 | * self-executing function to organize otherwise inline constructor code 6 | * @param {Object} args arguments passed to the controller 7 | */ 8 | (function constructor(args) { 9 | 10 | // use strict mode for this function scope 11 | 'use strict'; 12 | 13 | if (OS_WINDOWS) { 14 | $.refreshButton = Ti.UI.Windows.createAppBarButton({ icon: Ti.UI.Windows.SystemIcon.REFRESH }); 15 | $.refreshButton.addEventListener('click', function(e) { 16 | refresh(); 17 | }); 18 | $.commandBar.items = [ 19 | $.refreshButton 20 | ]; 21 | } 22 | 23 | // use the refresh callback for the initial load 24 | refresh(); 25 | 26 | // execute constructor with optional arguments passed to controller 27 | })(arguments[0] || {}); 28 | 29 | /** 30 | * event listener added via view for the refreshControl (iOS) or button (Android) 31 | * @param {Object} e Event, unless it was called from the constructor 32 | */ 33 | function refresh(e) { 34 | 'use strict'; 35 | 36 | // if we were called from the constructor programmatically show the refresh animation 37 | if (OS_IOS && !e) { 38 | $.refreshControl.beginRefreshing(); 39 | } 40 | 41 | /** 42 | * callback for fetch, both success and error 43 | * @param {Object} e Event 44 | */ 45 | function afterFetch(col, res) { 46 | 47 | // for iOS end the refreshing animation 48 | if (OS_IOS) { 49 | $.refreshControl.endRefreshing(); 50 | } 51 | } 52 | 53 | // MobileWeb can't load the remote file because we don't have access control set-up 54 | var url = OS_MOBILEWEB ? Ti.Filesystem.resourcesDirectory + 'feed.xml' : Alloy.CFG.url; 55 | 56 | // let the collection fetch data from it's data source 57 | Alloy.Collections.feed.fetch({ 58 | url: url, 59 | success: afterFetch, 60 | error: afterFetch 61 | }); 62 | } 63 | 64 | /** 65 | * set via view to be applied on each model before it renders 66 | * @param {Object} model BackBone model 67 | * @return {Object} Transformed properties 68 | */ 69 | function transform(model) { 70 | 'use strict'; 71 | 72 | var transformed = model.toJSON(); 73 | 74 | // return a formatted version of pubDate 75 | transformed.pubDate = moment(model.get('pubDate'), 'DD MMM YYYY HH:mm:ss ZZ').format('LLL'); 76 | 77 | return transformed; 78 | } 79 | 80 | /** 81 | * event listener set via view for when the user selects a ListView item 82 | * @param {Object} e Event 83 | */ 84 | function select(e) { 85 | 'use strict'; 86 | 87 | // we've stored the guid in the special itemId property of the item 88 | var guid = OS_MOBILEWEB ? e.row.itemId : e.itemId; 89 | 90 | // lookup the model 91 | var model = Alloy.Collections.feed.get(guid); 92 | 93 | // trigger the select event on this controller, passing the model with it 94 | // the index controller has an event listener for this event 95 | $.trigger('select', { 96 | model: model 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /app/lib/alloy/sync/rss.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require("alloy/underscore")._; 4 | 5 | /** 6 | * Called to sync 7 | * @param {string} method Sync method (only `read` is supported) 8 | * @param {Object} model Model/Collection to sync 9 | * @param {Object} opts Options passed to the `.fetch()` call 10 | */ 11 | module.exports.sync = function Sync(method, model, opts) { 12 | var url; 13 | 14 | if (method === 'read') { 15 | url = opts.url || model.config.adapter.url; 16 | 17 | /** 18 | * Handles the received XML and parses it to models 19 | * @param {string} err Error 20 | * @param {Object} xml Ti.XML.Document 21 | */ 22 | loadUrl(url, function onLoad(err, xml) { 23 | 24 | if (err) { 25 | return opts.error && opts.error(err); 26 | } 27 | 28 | try { 29 | 30 | // parse the Ti.XML.Document to an array of objects 31 | var data = parseXML(xml); 32 | 33 | opts.success && opts.success(data.length === 1 ? data[0] : data); 34 | 35 | model.trigger('fetch'); 36 | 37 | // catch any exceptions thrown 38 | } catch (e) { 39 | return opts.error && opts.error(e); 40 | } 41 | 42 | }); 43 | 44 | } else { 45 | throw 'Unsupported operation.'; 46 | } 47 | }; 48 | 49 | /** 50 | * Executes after a model has been created 51 | * @param {Object} Model Model object 52 | * @param {string} name Name of the model 53 | * @return {Object} Modified model object 54 | */ 55 | module.exports.afterModelCreate = function afterModelCreate(Model, name) { 56 | Model = Model || {}; 57 | Model.prototype.idAttribute = Model.prototype.config.adapter.idAttribute; 58 | return Model; 59 | }; 60 | 61 | /** 62 | * Parses an Ti.XML.Document to an array of objects 63 | * @param {Object} xml Ti.XML.Document 64 | * @return {[Object]} Array of objects 65 | */ 66 | function parseXML(xml) { 67 | var models = []; 68 | 69 | var elements = xml.documentElement.getElementsByTagName('item'); 70 | 71 | // for each 72 | for (var i = 0; i < elements.length; i++) { 73 | var element = elements.item(i); 74 | var model = {}; 75 | 76 | var childNodes = element.childNodes; 77 | var child; 78 | 79 | // for all childNodes 80 | for (var j = 0; j < childNodes.length; j++) { 81 | child = childNodes.item(j); 82 | 83 | // if the child is an element containing a text or CDATA node 84 | if (child.nodeType === child.ELEMENT_NODE && child.childNodes.length === 1 && [child.TEXT_NODE, child.CDATA_SECTION_NODE].indexOf(child.childNodes.item(0).nodeType) !== -1) { 85 | 86 | // set or append if model already has a property with the same name 87 | model[child.nodeName] = model[child.nodeName] ? (_.isArray(model[child.nodeName]) ? model[child.nodeName] : [model[child.nodeName]]).concat(child.textContent) : child.textContent; 88 | } 89 | } 90 | 91 | models.push(model); 92 | } 93 | 94 | return models; 95 | } 96 | 97 | /** 98 | * Loads an RSS string or URL 99 | * @param {string} url URL or local path to load 100 | * @param {Function} callback Callback (error, data) to call when the URL has been loaded and parsed 101 | */ 102 | function loadUrl(url, callback) { 103 | var xml; 104 | 105 | // assume it to be a local path 106 | if (url.indexOf('htt') !== 0) { 107 | 108 | // asyncify this sync code block for consistent behavior 109 | return setTimeout(function () { 110 | 111 | var file = Ti.Filesystem.getFile(url); 112 | 113 | if (!file.exists() || !file.isFile()) { 114 | return callback('URL is not a file.'); 115 | } 116 | 117 | var text = file.read().text; 118 | 119 | try { 120 | 121 | // parse the string to a Ti.XML.Document 122 | xml = Ti.XML.parseString(text); 123 | 124 | return callback(null, xml); 125 | 126 | // catch any exceptions thrown 127 | } catch (e) { 128 | return callback(e); 129 | } 130 | 131 | }, 0); 132 | } 133 | 134 | // fetch the URL 135 | var xhr = Ti.Network.createHTTPClient({ 136 | 137 | /** 138 | * Handle response 139 | * @param {Object} e Event 140 | */ 141 | onload: function onload(e) { 142 | var xml = this.responseXML; 143 | 144 | // response was no XML 145 | if (xml === null || xml.documentElement === null) { 146 | return callback(String.format('Response did not contain XML: %s', url)); 147 | } 148 | 149 | callback(null, xml); 150 | }, 151 | 152 | /** 153 | * Handle error 154 | * @param {Object} e Error 155 | */ 156 | onerror: function onerror(e) { 157 | callback(String.format('Request failed: ' + e.error)); 158 | } 159 | }); 160 | 161 | xhr.open('GET', url); 162 | xhr.send(); 163 | } 164 | -------------------------------------------------------------------------------- /plugins/ti.alloy/plugin.py: -------------------------------------------------------------------------------- 1 | import os, sys, subprocess, hashlib 2 | 3 | import subprocess 4 | 5 | def check_output(*popenargs, **kwargs): 6 | r"""Run command with arguments and return its output as a byte string. 7 | 8 | Backported from Python 2.7 as it's implemented as pure python on stdlib. 9 | 10 | >>> check_output(['/usr/bin/python', '--version']) 11 | Python 2.6.2 12 | """ 13 | process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) 14 | output, unused_err = process.communicate() 15 | retcode = process.poll() 16 | if retcode: 17 | cmd = kwargs.get("args") 18 | if cmd is None: 19 | cmd = popenargs[0] 20 | error = subprocess.CalledProcessError(retcode, cmd) 21 | error.output = output 22 | raise error 23 | return output 24 | 25 | def compile(config): 26 | paths = {} 27 | binaries = ["alloy","node"] 28 | 29 | dotAlloy = os.path.abspath(os.path.join(config['project_dir'], 'build', '.alloynewcli')) 30 | if os.path.exists(dotAlloy): 31 | print "[DEBUG] build/.alloynewcli file found, skipping plugin..." 32 | os.remove(dotAlloy) 33 | else: 34 | for binary in binaries: 35 | try: 36 | # see if the environment variable is defined 37 | paths[binary] = os.environ["ALLOY_" + ("NODE_" if binary == "node" else "") + "PATH"] 38 | except KeyError as ex: 39 | # next try PATH, and then our guess paths 40 | if sys.platform == "darwin" or sys.platform.startswith('linux'): 41 | userPath = os.environ["HOME"] 42 | guessPaths = [ 43 | "/usr/local/bin/"+binary, 44 | "/opt/local/bin/"+binary, 45 | userPath+"/local/bin/"+binary, 46 | "/opt/bin/"+binary, 47 | "/usr/bin/"+binary, 48 | "/usr/local/share/npm/bin/"+binary 49 | ] 50 | 51 | try: 52 | binaryPath = check_output(["which",binary], stderr=subprocess.STDOUT).strip() 53 | print "[DEBUG] %s installed at '%s'" % (binary,binaryPath) 54 | except: 55 | print "[WARN] Couldn't find %s on your PATH:" % binary 56 | print "[WARN] %s" % os.environ["PATH"] 57 | print "[WARN]" 58 | print "[WARN] Checking for %s in a few default locations:" % binary 59 | for p in guessPaths: 60 | sys.stdout.write("[WARN] %s -> " % p) 61 | if os.path.exists(p): 62 | binaryPath = p 63 | print "FOUND" 64 | break 65 | else: 66 | print "not found" 67 | binaryPath = None 68 | 69 | if binaryPath is None: 70 | print "[ERROR] Couldn't find %s" % binary 71 | sys.exit(1) 72 | else: 73 | paths[binary] = binaryPath 74 | 75 | # no guesses on windows, just use the PATH 76 | elif sys.platform == "win32": 77 | paths["alloy"] = "alloy.cmd" 78 | 79 | f = os.path.abspath(os.path.join(config['project_dir'], 'app')) 80 | if os.path.exists(f): 81 | print "[INFO] alloy app found at %s" % f 82 | rd = os.path.abspath(os.path.join(config['project_dir'], 'Resources')) 83 | 84 | devicefamily = 'none' 85 | simtype = 'none' 86 | version = '0' 87 | deploytype = 'development' 88 | 89 | if config['platform']==u'ios': 90 | version = config['iphone_version'] 91 | devicefamily = config['devicefamily'] 92 | deploytype = config['deploytype'] 93 | if config['platform']==u'android': 94 | builder = config['android_builder'] 95 | version = builder.tool_api_level 96 | deploytype = config['deploy_type'] 97 | if config['platform']==u'mobileweb': 98 | builder = config['mobileweb_builder'] 99 | deploytype = config['deploytype'] 100 | 101 | cfg = "platform=%s,version=%s,simtype=%s,devicefamily=%s,deploytype=%s," % (config['platform'],version,simtype,devicefamily,deploytype) 102 | 103 | if sys.platform == "win32": 104 | cmd = [paths["alloy"], "compile", f, "--no-colors", "--config", cfg] 105 | else: 106 | cmd = [paths["node"], paths["alloy"], "compile", f, "--no-colors", "--config", cfg] 107 | 108 | print "[INFO] Executing Alloy compile:" 109 | print "[INFO] %s" % " ".join(cmd) 110 | 111 | try: 112 | print check_output(cmd, stderr=subprocess.STDOUT) 113 | except subprocess.CalledProcessError as ex: 114 | if hasattr(ex, 'output'): 115 | print ex.output 116 | print "[ERROR] Alloy compile failed" 117 | retcode = 1 118 | if hasattr(ex, 'returncode'): 119 | retcode = ex.returncode 120 | sys.exit(retcode) 121 | except EnvironmentError as ex: 122 | print "[ERROR] Unexpected error with Alloy compiler plugin: %s" % ex.strerror 123 | sys.exit(2) 124 | -------------------------------------------------------------------------------- /plugins/ti.alloy/hooks/alloy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Alloy 3 | * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved. 4 | * See LICENSE for more information on licensing. 5 | */ 6 | 7 | exports.cliVersion = '>=3.X'; 8 | exports.version = '1.0.1'; 9 | var SILENT = true; 10 | 11 | exports.init = function (logger, config, cli, appc) { 12 | var path = require('path'), 13 | fs = require('fs'), 14 | afs = appc.fs, 15 | i18n = appc.i18n(__dirname), 16 | __ = i18n.__, 17 | __n = i18n.__n, 18 | pkginfo = appc.pkginfo.package(module), 19 | exec = require('child_process').exec, 20 | spawn = require('child_process').spawn, 21 | parallel = appc.async.parallel; 22 | 23 | if (!process.env.sdk) { 24 | process.env.sdk = cli.sdk.name; 25 | } 26 | 27 | function run(deviceFamily, deployType, target, finished, silent) { 28 | var appDir = path.join(cli.argv['project-dir'], 'app'); 29 | if (!afs.exists(appDir)) { 30 | logger.info(__('Project not an Alloy app, continuing')); 31 | finished(); 32 | return; 33 | } 34 | logger.info(__('Found Alloy app in %s', appDir.cyan)); 35 | 36 | // TODO: Make this check specific to a TiSDK version 37 | // create a .alloynewcli file to tell old plugins not to run 38 | var buildDir = path.join(cli.argv['project-dir'], 'build'); 39 | if (!afs.exists(buildDir)) { 40 | fs.mkdirSync(buildDir); 41 | } 42 | fs.writeFileSync(path.join(buildDir, '.alloynewcli'), ''); 43 | 44 | var cRequire = afs.resolvePath(__dirname, '..', 'Alloy', 'commands', 'compile', 'index.js'), 45 | config = { 46 | platform: /(?:iphone|ipad)/.test(cli.argv.platform) ? 'ios' : cli.argv.platform, 47 | version: '0', 48 | simtype: 'none', 49 | devicefamily: /(?:iphone|ios)/.test(cli.argv.platform) ? deviceFamily : 'none', 50 | deploytype: deployType || cli.argv['deploy-type'] || 'development', 51 | target: target 52 | }; 53 | if (silent) { 54 | // turn off all logging output for code analyzer build hook 55 | config.noBanner = 'true'; 56 | config.logLevel = '-1'; 57 | } 58 | 59 | if (cli.argv.theme) { 60 | config.theme = cli.argv.theme; 61 | } 62 | 63 | config = Object.keys(config).map(function (c) { 64 | return c + '=' + config[c]; 65 | }).join(','); 66 | 67 | if (afs.exists(cRequire)) { 68 | // we're being invoked from the actual alloy directory! 69 | // no need to subprocess, just require() and run 70 | var origLimit = Error.stackTraceLimit; 71 | Error.stackTraceLimit = Infinity; 72 | try { 73 | require(cRequire)({}, { 74 | config: config, 75 | outputPath: cli.argv['project-dir'], 76 | _version: pkginfo.version 77 | }); 78 | } catch (e) { 79 | logger.error(__('Alloy compiler failed')); 80 | e.toString().split('\n').forEach(function (line) { 81 | if (line) { logger.error(line); } 82 | }); 83 | process.exit(1); 84 | } 85 | Error.stackTraceLimit = origLimit; 86 | finished(); 87 | } else { 88 | // we have no clue where alloy is installed, so we're going to subprocess 89 | // alloy and hope it's in the system path or a well known place 90 | var paths = {}; 91 | var locatorCmd = process.platform === 'win32' ? 'where' : 'which'; 92 | parallel(this, ['alloy', 'node'].map(function (bin) { 93 | return function (done) { 94 | var envName = 'ALLOY_' + (bin === 'node' ? 'NODE_' : '') + 'PATH'; 95 | 96 | paths[bin] = process.env[envName]; 97 | if (paths[bin]) { 98 | done(); 99 | } else if (process.platform === 'win32' && bin === 'alloy') { 100 | paths.alloy = 'alloy.cmd'; 101 | done(); 102 | } else { 103 | exec(locatorCmd + ' ' + bin, function (err, stdout, strerr) { 104 | if (!err) { 105 | paths[bin] = stdout.trim(); 106 | done(); 107 | } else { 108 | parallel(this, [ 109 | '/usr/local/bin/' + bin, 110 | '/opt/local/bin/' + bin, 111 | path.join(process.env.HOME, 'local/bin', bin), 112 | '/opt/bin/' + bin, 113 | '/usr/bin/' + bin 114 | ].map(function (p) { 115 | return function (cb) { 116 | if (afs.exists(p)) { paths[bin] = p; } 117 | cb(); 118 | }; 119 | }), done); 120 | } 121 | }); 122 | } 123 | }; 124 | }), function () { 125 | 126 | // compose alloy command execution 127 | var cmd = [paths.node, paths.alloy, 'compile', appDir, '--config', config]; 128 | if (cli.argv['no-colors'] || cli.argv['color'] === false) { cmd.push('--no-colors'); } 129 | 130 | // process each line of output from alloy 131 | function checkLine(line) { 132 | var re = new RegExp( 133 | '^(?:\u001b\\[\\d+m)?\\[?(' + 134 | logger.getLevels().join('|') + 135 | ')\\]?\s*(?:\u001b\\[\\d+m)?(.*)', 'i' 136 | ); 137 | if (line) { 138 | var m = line.match(re); 139 | if (m) { 140 | logger[m[1].toLowerCase()](m[2].trim()); 141 | } else { 142 | logger.debug(line); 143 | } 144 | } 145 | } 146 | 147 | // execute alloy in os-specific manner 148 | var child; 149 | if (process.platform === 'win32' && paths.alloy === 'alloy.cmd') { 150 | cmd.shift(); 151 | logger.info(__('Executing Alloy compile: %s', 152 | ['cmd', '/s', '/c'].concat(cmd).join(' ').cyan)); 153 | 154 | // arg processing from https://github.com/MarcDiethelm/superspawn 155 | child = spawn('cmd', [['/s', '/c', '"' + 156 | cmd.map(function(a) { 157 | if (/^[^"].* .*[^"]/.test(a)) return '"' + a + '"'; return a; 158 | }).join(' ') + '"'].join(' ')], { 159 | stdio: 'inherit', 160 | windowsVerbatimArguments: true 161 | }); 162 | } else { 163 | logger.info(__('Executing Alloy compile: %s', cmd.join(' ').cyan)); 164 | child = spawn(cmd.shift(), cmd); 165 | child.stdout.on('data', function (data) { 166 | data.toString().split('\n').forEach(checkLine); 167 | }); 168 | child.stderr.on('data', function (data) { 169 | data.toString().split('\n').forEach(checkLine); 170 | }); 171 | } 172 | 173 | // handle the completion of alloy, success or otherwise 174 | child.on('exit', function (code) { 175 | if (code) { 176 | logger.error(__('Alloy compiler failed')); 177 | process.exit(1); 178 | } else { 179 | logger.info(__('Alloy compiler completed successfully')); 180 | 181 | afs.exists(path.join(cli.argv['project-dir'], 'build', 'i18n')) && process.argv.push('--i18n-dir', 'build'); 182 | afs.exists(path.join(cli.argv['project-dir'], 'build', 'platform')) && (cli.argv['platform-dir'] = 'build/platform'); 183 | } 184 | finished(); 185 | }); 186 | 187 | }); 188 | } 189 | } 190 | 191 | cli.addHook('build.pre.compile', function (build, finished) { 192 | var deployType = build.deployType, 193 | target = build.target; 194 | 195 | run(build.deviceFamily, deployType, target, finished); 196 | }); 197 | 198 | cli.addHook('codeprocessor.pre.run', function (build, finished) { 199 | run('none', 'development', undefined, finished, SILENT); 200 | }); 201 | }; 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Titanium Alloy RSS Sample 2 | > **OR:** An example of a *master > detail* app. 3 | 4 | This is a Titanium Alloy sample app that creates a RSS reader. The app lets you pull a live RSS feed from the internet, list the articles and then drill down to the article itself. 5 | 6 | ![screenshots](screenshots.png) 7 | 8 | As you'll [see](app/controllers/index.js) the code is heavily documented to tell you exactly what is going on. 9 | 10 | Let's run through some of the main topics covered. 11 | 12 | ## Custom Android Material Theme 13 | For Android, the app uses a [custom Android Material Theme](http://docs.appcelerator.com/platform/latest/#!/guide/Android_Themes-section-34636181_AndroidThemes-MaterialTheme). Material made it much easier to create custom Android themes and all you have to do is override some of the default colors in [platform/android/res/values/custom_theme.xml](platform/android/res/values/custom_theme.xml) and select the style name in [tiapp.xml](tiapp.xml) under *android/manifest/application@android:theme*. 14 | 15 | * Guide: [Android Themes](http://docs.appcelerator.com/platform/latest/#!/guide/Android_Themes) 16 | 17 | ## Config 18 | We've made it very easy to change the URL for the RSS feed without touching the code. It's set in [app/config.json](alloy/app/config.json) under *global/url* and read as `Alloy.CFG.url` in [app/controllers/master.js](app/controllers/master.js). As you can read in the guide, we could even set a different value for this propery based on the platform and environment the app runs on. 19 | 20 | * Guide: [Project Configuration File (config.json)](http://docs.appcelerator.com/platform/latest/#!/guide/Project_Configuration_File_(config.json)) 21 | 22 | ## Global Styles 23 | For iOS we need to set the colors for our toolbars on each Window. Fortunately we can use [app/styles/app.tss](app/styles/app.tss) to set styles that apply to all views. This is also a great place to reset/normalize, like we do for Labels, which are by default `white` on Windows and some `grey` on Android. 24 | 25 | * Guide: [Alloy Styles and Themes](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Styles_and_Themes) 26 | 27 | ## Conditional Code 28 | As the screenshots show, we use platform and even device specific UI elements to provide the best user experience. 29 | 30 | We could use Titanium and Alloy's support for [platform-specific resources](http://docs.appcelerator.com/platform/latest/#!/guide/Supporting_Multiple_Platforms_in_a_Single_Codebase-section-29004890_SupportingMultiplePlatformsinaSingleCodebase-Platform-specificresources) and create an Android-specific view in `app/views/android/index.xml` and an iOS-specific view in `app/views/iphone/index.xml`. 31 | 32 | In this case we chose to use Alloy's support for [conditional code](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_XML_Markup-section-35621528_AlloyXMLMarkup-ConditionalCode) to keep them all in one view. We would have needed this for an iPhone and iPad specific view anyway and this way you can see how we handle all platforms and form factors in one glance. 33 | 34 | You will see we use conditional code in the other two [views](app/views) as well to use platform-specific UI elements like Android ActionBar menu items and a RefreshControl for iOS. 35 | 36 | * Guides: 37 | * [Platform-specific resources](http://docs.appcelerator.com/platform/latest/#!/guide/Supporting_Multiple_Platforms_in_a_Single_Codebase-section-29004890_SupportingMultiplePlatformsinaSingleCodebase-Platform-specificresources) 38 | * [Conditional Code](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_XML_Markup-section-35621528_AlloyXMLMarkup-ConditionalCode) 39 | 40 | ## Require 41 | To keep our code [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself) we create a seperate [master](app/views/master.xml) controller for the list. As you can see, we require this controller in all four different navigation patterns in [app/views/index.xml](app/views/index.xml). 42 | 43 | * Guide: [Require Element](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_XML_Markup-section-35621528_AlloyXMLMarkup-RequireElement) 44 | 45 | ## Navigation Patterns 46 | The app uses the following navigation patterns: 47 | 48 | * [`NavigationWindow`](http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.iOS.NavigationWindow) for iPhone and the similar [`NavigationGroup`](http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.MobileWeb.NavigationGroup) for MobileWeb. The classic example of this pattern is the *Settings App* on iOS. It stacks multiple windows, showing the title of the current window in a navigation bar that also has a *Back* button to pop back to the previous one. Ideal for hierarchies of two or more levels like our *master > detail*. 49 | * [`SplitWindow`](http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.iOS.SplitWindow) for iPad is a pattern you will know from the *Mail App* on iOS. It displays two windows, one narrow and the other wide. Ideal for a single level *master > detail* app on bigger screens. 50 | * And then finally, for Android we simply open windows on top of each other without managing much about them. And for Android this works just great because it has hardware buttons for navigation back as well as an [Action Bar](http://docs.appcelerator.com/platform/latest/#!/guide/Android_Action_Bar) that we can set to display a back button. 51 | 52 | ## ListView vs TableView 53 | In [app/views/master.xml](https://github.com/appcelerator-developer-relations/Sample.RSS/blob/alloy/app/views/master.xml) you can see we use a ListView for iOS and Android and a TableView for MobileWeb, which does support ListViews. 54 | 55 | [ListView](http://docs.appcelerator.com/platform/latest/#!/guide/ListViews) is the more efficient and faster successor of TableView, but there are some specific use cases for which we keep the TableView around for all platforms. The main difference is that ListViews use templates and data while TableViews are composed of regular views. 56 | 57 | ListViews can be more difficult to implement in Titanium, but as you can see [Alloy hides most of that complexity](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_ListView_Guide) from you. So if you can, always use ListViews instead of TableViews. 58 | 59 | * Guides: 60 | * [ListViews](http://docs.appcelerator.com/platform/latest/#!/guide/ListViews) 61 | * [Alloy ListView Guide](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_ListView_Guide) 62 | 63 | ## Collections, Models & Data-Binding 64 | Perhaps the most complex part of our app is the use of Alloy Collections, Models and data-binding to fetch and display the RSS feed. It is really just seen as complex because Alloy does most of the work, which makes it feel like magic. 65 | 66 | Embrace the magic and unleash the power of [BackBone.js](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Models) in Alloy: 67 | 68 | ### Models 69 | [Models](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Collection_and_Model_Objects-section-36739589_AlloyCollectionandModelObjects-Models) can be seen as the rows of a table and their definition configures the columns and the connection to where the rows are read from and synced to. The columns are not required for all types of sync adapters and as you can see our [app/models/feed.js](app/models/feed.js) only has the configuration for our custom RSS sync adapter. 70 | 71 | ### Sync adapters 72 | Alloy comes with a few [ready-made sync adapters](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Sync_Adapters_and_Migrations) of which the one for SQLite is probably the most interesting one. You can add custom sync adapters by dropping a CommonJS module that follows the expected [signature](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Sync_Adapters_and_Migrations-section-36739597_AlloySyncAdaptersandMigrations-CustomSyncAdapters) in `app/lib/alloy/sync` and use the filename in the model definition. A popular community adapter is [restapi](https://github.com/viezel/napp.alloy.adapter.restapi) by Mads Möller. 73 | 74 | For this app, we created a custom [rss adapter](https://github.com/appcelerator-developer-relations/Sample.RSS/blob/alloy/app/lib/alloy/sync/rss.js) that reads a URL or path to a local file. For MobileWeb we use a local file because our RSS feed currently doesn't send a `Access-Control-Allow-Origin` header. When it is done, it calls the `opts.success` callback with the fetched data and BackBone will create and collect the models. We also fire a `fetch` event so that our data binding is triggered. 75 | 76 | ### Collections 77 | [Collections](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Collection_and_Model_Objects-section-36739589_AlloyCollectionandModelObjects-Collections) can be seen as tables. They keep models and fire events if there are any changes. This is exactly what we need for data binding. 78 | 79 | ### Data Binding 80 | [Data binding](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Data_Binding) simply comes down to listening to changes on an object and updating the UI accordingly. To do this we add a `dataCollection` tag to a [view element](app/views/master.xml) and Alloy will use that element's children to populate the view for each model in the collection. 81 | 82 | In the master [controller](app/controllers/master.js) we can now call the collection's `fetch()` method to have it populate itself using the rss sync adapter which in turn will trigger the data binding to display them in the list. As you can see we also have the option to transform the data by setting a `dataTransform` function in the view. 83 | 84 | * Guide: [Alloy Models](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Models), also on Collections, Data Binding and Sync Adapters 85 | 86 | ## Opening the detail view 87 | The last piece of our puzzle is opening an article from our list. The [master view](app/views/master.xml) sets a `select` callback to be called when the user taps on a row. You can also see that it binds the identifying `guid` field of the models to a special `itemId` attribute of our rows. 88 | 89 | The [master controller](app/controllers/master.js) defines the callback, receives the id of the model that comes with the event, looks up the model and then fires an event on the controller itself. We haven't talked too much about it, but every [controller](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Controllers) extends the `BackBone.Events` prototype, which allows it to dispatch events. 90 | 91 | The reason we don't open the detail window directly from masters is that it depends on the navigation pattern how exactly we should. It's a best practice to keep controllers unaware of their context so we can re-use them in different contexts. So in the [index view](app/views/index.xml) we bind an `onSelect` listener to the `select` event of the master controller. In the [index controller](app/controllers/index.js) you will see that this again uses [Conditional Code](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Controllers-section-34636384_AlloyControllers-ConditionalCode) to determine how exactly to open the detail window, passing the model to the detail controller. 92 | 93 | * Guides: 94 | * [Event Handling](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_XML_Markup-section-35621528_AlloyXMLMarkup-EventHandling) in Alloy views 95 | * [Alloy Controllers](http://docs.appcelerator.com/platform/latest/#!/guide/Alloy_Controllers) 96 | 97 | ## WebViews 98 | The [detail controller](app/controllers/detail.js) then receives the model and uses it to set the window title and WebView URL. The [WebView](http://docs.appcelerator.com/platform/latest/#!/guide/The_WebView_Component) can be seen as a chromeless browser, embedded in the app. Though one of Titanium's unique features is the use of native UI components, you can easily intergrate existing local or remote HTML and even [communicate](http://docs.appcelerator.com/platform/latest/#!/guide/Communication_Between_WebViews_and_Titanium) with it. 99 | 100 | * Guide: [Integrating Web Content](http://docs.appcelerator.com/platform/latest/#!/guide/Integrating_Web_Content) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Appcelerator, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | (or the full text of the license is below) 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | 18 | 19 | Apache License 20 | Version 2.0, January 2004 21 | http://www.apache.org/licenses/ 22 | 23 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 24 | 25 | 1. Definitions. 26 | 27 | "License" shall mean the terms and conditions for use, reproduction, 28 | and distribution as defined by Sections 1 through 9 of this document. 29 | 30 | "Licensor" shall mean the copyright owner or entity authorized by 31 | the copyright owner that is granting the License. 32 | 33 | "Legal Entity" shall mean the union of the acting entity and all 34 | other entities that control, are controlled by, or are under common 35 | control with that entity. For the purposes of this definition, 36 | "control" means (i) the power, direct or indirect, to cause the 37 | direction or management of such entity, whether by contract or 38 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 39 | outstanding shares, or (iii) beneficial ownership of such entity. 40 | 41 | "You" (or "Your") shall mean an individual or Legal Entity 42 | exercising permissions granted by this License. 43 | 44 | "Source" form shall mean the preferred form for making modifications, 45 | including but not limited to software source code, documentation 46 | source, and configuration files. 47 | 48 | "Object" form shall mean any form resulting from mechanical 49 | transformation or translation of a Source form, including but 50 | not limited to compiled object code, generated documentation, 51 | and conversions to other media types. 52 | 53 | "Work" shall mean the work of authorship, whether in Source or 54 | Object form, made available under the License, as indicated by a 55 | copyright notice that is included in or attached to the work 56 | (an example is provided in the Appendix below). 57 | 58 | "Derivative Works" shall mean any work, whether in Source or Object 59 | form, that is based on (or derived from) the Work and for which the 60 | editorial revisions, annotations, elaborations, or other modifications 61 | represent, as a whole, an original work of authorship. For the purposes 62 | of this License, Derivative Works shall not include works that remain 63 | separable from, or merely link (or bind by name) to the interfaces of, 64 | the Work and Derivative Works thereof. 65 | 66 | "Contribution" shall mean any work of authorship, including 67 | the original version of the Work and any modifications or additions 68 | to that Work or Derivative Works thereof, that is intentionally 69 | submitted to Licensor for inclusion in the Work by the copyright owner 70 | or by an individual or Legal Entity authorized to submit on behalf of 71 | the copyright owner. For the purposes of this definition, "submitted" 72 | means any form of electronic, verbal, or written communication sent 73 | to the Licensor or its representatives, including but not limited to 74 | communication on electronic mailing lists, source code control systems, 75 | and issue tracking systems that are managed by, or on behalf of, the 76 | Licensor for the purpose of discussing and improving the Work, but 77 | excluding communication that is conspicuously marked or otherwise 78 | designated in writing by the copyright owner as "Not a Contribution." 79 | 80 | "Contributor" shall mean Licensor and any individual or Legal Entity 81 | on behalf of whom a Contribution has been received by Licensor and 82 | subsequently incorporated within the Work. 83 | 84 | 2. Grant of Copyright License. Subject to the terms and conditions of 85 | this License, each Contributor hereby grants to You a perpetual, 86 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 87 | copyright license to reproduce, prepare Derivative Works of, 88 | publicly display, publicly perform, sublicense, and distribute the 89 | Work and such Derivative Works in Source or Object form. 90 | 91 | 3. Grant of Patent License. Subject to the terms and conditions of 92 | this License, each Contributor hereby grants to You a perpetual, 93 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 94 | (except as stated in this section) patent license to make, have made, 95 | use, offer to sell, sell, import, and otherwise transfer the Work, 96 | where such license applies only to those patent claims licensable 97 | by such Contributor that are necessarily infringed by their 98 | Contribution(s) alone or by combination of their Contribution(s) 99 | with the Work to which such Contribution(s) was submitted. If You 100 | institute patent litigation against any entity (including a 101 | cross-claim or counterclaim in a lawsuit) alleging that the Work 102 | or a Contribution incorporated within the Work constitutes direct 103 | or contributory patent infringement, then any patent licenses 104 | granted to You under this License for that Work shall terminate 105 | as of the date such litigation is filed. 106 | 107 | 4. Redistribution. You may reproduce and distribute copies of the 108 | Work or Derivative Works thereof in any medium, with or without 109 | modifications, and in Source or Object form, provided that You 110 | meet the following conditions: 111 | 112 | (a) You must give any other recipients of the Work or 113 | Derivative Works a copy of this License; and 114 | 115 | (b) You must cause any modified files to carry prominent notices 116 | stating that You changed the files; and 117 | 118 | (c) You must retain, in the Source form of any Derivative Works 119 | that You distribute, all copyright, patent, trademark, and 120 | attribution notices from the Source form of the Work, 121 | excluding those notices that do not pertain to any part of 122 | the Derivative Works; and 123 | 124 | (d) If the Work includes a "NOTICE" text file as part of its 125 | distribution, then any Derivative Works that You distribute must 126 | include a readable copy of the attribution notices contained 127 | within such NOTICE file, excluding those notices that do not 128 | pertain to any part of the Derivative Works, in at least one 129 | of the following places: within a NOTICE text file distributed 130 | as part of the Derivative Works; within the Source form or 131 | documentation, if provided along with the Derivative Works; or, 132 | within a display generated by the Derivative Works, if and 133 | wherever such third-party notices normally appear. The contents 134 | of the NOTICE file are for informational purposes only and 135 | do not modify the License. You may add Your own attribution 136 | notices within Derivative Works that You distribute, alongside 137 | or as an addendum to the NOTICE text from the Work, provided 138 | that such additional attribution notices cannot be construed 139 | as modifying the License. 140 | 141 | You may add Your own copyright statement to Your modifications and 142 | may provide additional or different license terms and conditions 143 | for use, reproduction, or distribution of Your modifications, or 144 | for any such Derivative Works as a whole, provided Your use, 145 | reproduction, and distribution of the Work otherwise complies with 146 | the conditions stated in this License. 147 | 148 | 5. Submission of Contributions. Unless You explicitly state otherwise, 149 | any Contribution intentionally submitted for inclusion in the Work 150 | by You to the Licensor shall be under the terms and conditions of 151 | this License, without any additional terms or conditions. 152 | Notwithstanding the above, nothing herein shall supersede or modify 153 | the terms of any separate license agreement you may have executed 154 | with Licensor regarding such Contributions. 155 | 156 | 6. Trademarks. This License does not grant permission to use the trade 157 | names, trademarks, service marks, or product names of the Licensor, 158 | except as required for reasonable and customary use in describing the 159 | origin of the Work and reproducing the content of the NOTICE file. 160 | 161 | 7. Disclaimer of Warranty. Unless required by applicable law or 162 | agreed to in writing, Licensor provides the Work (and each 163 | Contributor provides its Contributions) on an "AS IS" BASIS, 164 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 165 | implied, including, without limitation, any warranties or conditions 166 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 167 | PARTICULAR PURPOSE. You are solely responsible for determining the 168 | appropriateness of using or redistributing the Work and assume any 169 | risks associated with Your exercise of permissions under this License. 170 | 171 | 8. Limitation of Liability. In no event and under no legal theory, 172 | whether in tort (including negligence), contract, or otherwise, 173 | unless required by applicable law (such as deliberate and grossly 174 | negligent acts) or agreed to in writing, shall any Contributor be 175 | liable to You for damages, including any direct, indirect, special, 176 | incidental, or consequential damages of any character arising as a 177 | result of this License or out of the use or inability to use the 178 | Work (including but not limited to damages for loss of goodwill, 179 | work stoppage, computer failure or malfunction, or any and all 180 | other commercial damages or losses), even if such Contributor 181 | has been advised of the possibility of such damages. 182 | 183 | 9. Accepting Warranty or Additional Liability. While redistributing 184 | the Work or Derivative Works thereof, You may choose to offer, 185 | and charge a fee for, acceptance of support, warranty, indemnity, 186 | or other liability obligations and/or rights consistent with this 187 | License. However, in accepting such obligations, You may act only 188 | on Your own behalf and on Your sole responsibility, not on behalf 189 | of any other Contributor, and only if You agree to indemnify, 190 | defend, and hold each Contributor harmless for any liability 191 | incurred by, or claims asserted against, such Contributor by reason 192 | of your accepting any such warranty or additional liability. 193 | 194 | END OF TERMS AND CONDITIONS 195 | 196 | APPENDIX: How to apply the Apache License to your work. 197 | 198 | To apply the Apache License to your work, attach the following 199 | boilerplate notice, with the fields enclosed by brackets "[]" 200 | replaced with your own identifying information. (Don't include 201 | the brackets!) The text should be enclosed in the appropriate 202 | comment syntax for the file format. We also recommend that a 203 | file or class name and description of purpose be included on the 204 | same "printed page" as the copyright notice for easier 205 | identification within third-party archives. 206 | 207 | Copyright [yyyy] [name of copyright owner] 208 | 209 | Licensed under the Apache License, Version 2.0 (the "License"); 210 | you may not use this file except in compliance with the License. 211 | You may obtain a copy of the License at 212 | 213 | http://www.apache.org/licenses/LICENSE-2.0 214 | 215 | Unless required by applicable law or agreed to in writing, software 216 | distributed under the License is distributed on an "AS IS" BASIS, 217 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 218 | See the License for the specific language governing permissions and 219 | limitations under the License. 220 | -------------------------------------------------------------------------------- /app/assets/feed.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | Appcelerator Inc 12 | 13 | http://www.appcelerator.com 14 | The Mobile First Platform 15 | Thu, 14 May 2015 17:33:09 +0000 16 | en-US 17 | hourly 18 | 1 19 | 20 | 21 | The Week in Mobile: May 3-9, 2015 22 | http://www.appcelerator.com/blog/2015/05/week-mobile-google-works/ 23 | http://www.appcelerator.com/blog/2015/05/week-mobile-google-works/#comments 24 | Mon, 11 May 2015 16:00:55 +0000 25 | 26 | 27 | 28 | 29 | http://www.appcelerator.com/?p=39954 30 | Google works to attract Android devs, Pinterest releases API, Meerkat attempts a comeback and more]]> 31 |

Google works to attract Android devs, Pinterest releases API, Meerkat attempts a comeback and more


32 | 33 |

Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. This content is designed to inspire you and your company to take advantage of the many benefits mobile can offer.

34 | 35 |

Google Making Android App Dev More Appealing

36 | 37 |

Google is taking steps to make developing apps for Android more appealing. The company is expected to announce several changes to Google Play at its Google I/O developer conference for developers to explore, including new functionality that allows them to test different versions of their apps’ profile pages on the store. That means developers will have more visibility into what drives app downloads. 38 | 39 |

Additionally, Google is expected to debut Voice Access, which will allow developers to easily incorporate voice commands into their apps. With these updates and more expected to debut at the Google I/O Developer Event, it’s clear that the company is looking to boost its ecosystem by making Android app development just a little bit more enticing.

40 | 41 |

Pinterest Releases API for Developers

42 | 43 |

Pinterest announced the release of an API for developers, enabling them to build apps that use Pinterest data. The release comes long after companies such as Facebook and Twitter opened up their platforms to third-party developers. And Pinterest plans to take a slightly different approach: not everyone will have access to the API right away. For the time being, developers can sign up for access to the API and a select few will be granted permission based on their ability to bring new and innovative functionality to the Pinterest app. For example, an app for ordering groceries may be given early access to Pinterest’s API so that users can order ingredients for a recipe found on Pinterest.

44 | 45 |

Snapchat Adds Social to Discover in Hopes of Increasing Viewership

46 | 47 |

How do you revive a failed app functionality? Make it social. At least that seems to be Snapchat’s solution. In January, Snapchat launched Discover, a feature within the app that allows users to watch content from channels like CNN, Comedy Central and Food Network. Discover content viewership saw significant numbers during its first few days, however; it has dropped 30 to 50 percent since then.

48 | 49 |

Now, in an attempt to boost viewership, Snapchat has released an update that allows users to send clips from Discover to friends, which will include a “Sent via Discover” banner that links straight to Discover. It’s possible that this move toward interactive functionality and deeplinking may lead to further innovation, allowing users to do more than just watch or create.

50 | 51 |

Facebook Reports Over 1 Million Video Calls from Messenger in First Two Days Following Launch

52 | 53 |

Facebook reported that Messenger users made over 1 million video calls during the first two days after its launch. Released late last month, Messenger’s video calling functionality is currently available in 18 markets, including the UK, France, Greece, Mexico, Portugal and the United States. Additional markets are expected to join that list in the coming months.

54 | 55 |

The success of Messenger video calling follows the recent announcement that Messenger accounts for 10 percent of all VoIP calls worldwide. It seems Facebook is happy with its hotly debated decision to spin chat functionality off of the company’s main app. “If Messenger were still buried within the main app, video calling would be buried within that,” Messenger head of Product Stan Chudnovsky told Mashable.

56 | 57 |

Meerkat Plans its Comeback with New Features for iOS

58 | 59 |

Livestream video app Meerkat is making its comeback following Twitter’s launch of a competing app, Periscope, which, in many ways, has left Meerkat in its shadow. Meerkat’s latest update for iOS allows users to publish livestreams to Facebook, which means the app is no longer dependent on Twitter to attract users and can reach a larger audience across multiple networks. The new update also allows users to link the app with their phone contacts in order to find new livestreams. Additionally, Meerkat introduced two new features: a status-ranking functionality and a new way to react to livestreams with emojis.

60 | ]]>
61 | http://www.appcelerator.com/blog/2015/05/week-mobile-google-works/feed/ 62 | 0 63 |
64 | 65 | The Week in Mobile: April 26 – May 2, 2015 66 | http://www.appcelerator.com/blog/2015/05/week-mobile-apple-watch-google/ 67 | http://www.appcelerator.com/blog/2015/05/week-mobile-apple-watch-google/#comments 68 | Mon, 04 May 2015 16:00:51 +0000 69 | 70 | 71 | 72 | 73 | http://www.appcelerator.com/?p=39940 74 | Apple exceeds goal for Watch apps, 70 new apps gain access to Google Now, Windows 10 to support ported iOS and Android apps and more]]> 75 |

Apple exceeds goal for Watch apps, 70 new apps gain access to Google Now, Windows 10 to support ported iOS and Android apps and more


76 | 77 |

Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. This content is designed to inspire you and your company to take advantage of the many benefits mobile can offer.

78 | 79 |

Apple Exceeds Goal with Over 3,500 Apple Watch Apps at Launch

80 | 81 |

The Apple Watch debuted just over a week ago alongside more than 3,500 supporting apps. That far exceeds the company’s original goal, which was to have 1,000 apps ready by the launch date. The Apple Watch’s app catalogue outnumbers the 1,000 total apps ready for the iPad launch in 2010 and is 7 times greater than the 500 apps available for the iPhone when the App Store first launched in 2008. And when it comes to competing with other wearables’ apps, Apple is on track to dominate the market, considering it took Samsung’s Gear wearables app store 18 months to hit 3,000 apps. Because apps are ultimately the key to the Apple Watch’s success, Apple’s ability to seed the app store prior to its product launch bodes well for the success of the new device.

82 | 83 |

However… while there may be thousands of Apple Watch apps available for download, many are running slowly and taking a long time to load. Bugginess seems to be a real problem, but it’s likely apps will improve over the next few weeks as developers get a chance to see the Watch in action and make adjustments accordingly.

84 | 85 |

70 New Apps Gain Access to Google Now

86 | 87 |

Google Now, a tool for Android that helps users stay on top of their day by learning their behaviors and needs over time, opened its platform up to 70 additional third-party apps last week, bringing the total number of apps available to 110 and making the platform a lot more powerful. Google partnered with 40 third-party apps back in January and is continuing to expand the range and variety of its Google Now cards with this most recent roll out.

88 | 89 |

New partners include a range of companies including Zipcar, which will display upcoming reservation start and end times; Spotify, which will suggest playlists based on contextual information; and ABC News, which will share relevant breaking news. While Google increased the number of companies allowed access its second rollout, it appears the company will continue to gradually open the platform up to third parties rather than making the API open to the public.

90 | 91 |

Samsung Wants to Bring Wearables into the Workplace

92 | 93 |

Samsung is exploring what role its smartwatches and Gear VR headsets could play in the workplace. Right now, the devices are primarily purchased by consumers, but Samsung is looking to expand into the enterprise by working with developers on business-focused apps for its smartwatches and new content to drive headset usage. Samsung Vice President of Mobile Marketing Eric McCarty said smartwatches can provide alerts and immediate access to critical information and that VR headsets have the potential to present multimedia content in a new and powerful way.

94 | 95 |

Samsung envisions smartwatches as a tool to communicate more quickly and efficiently. For example, restaurants could use smartwatches to send service notifications to staff. While smartwatches offer an opportunity to improve speed and efficiency of communication, the Gear VR headset offers an opportunity to revolutionize the way we convey information. The device could be used as a training tool or to give virtual tours. For example, real estate agents could give virtual tours of homes for potential buyers.

96 | 97 |

Windows 10 to Support Ported Android and iOS Apps

98 | 99 |

In an attempt to ride the wave of Android and iOS success, Microsoft announced that app developers will now be able to reuse existing code written for these other platforms to create apps that run on Windows 10. That means developers will only need to make slight modifications to their pre-existing code in order for apps to function on Windows phones, tablets, PCs, Xbox One and HoloLens goggles. With a few slight adjustments, developers will be able to reach an audience that Microsoft hopes will extend to 1 billion devices over the next two or three years. Of course developers will still need to do the work to offer a truly native experience by coding for platform-specific features and nuances.

100 | ]]>
101 | http://www.appcelerator.com/blog/2015/05/week-mobile-apple-watch-google/feed/ 102 | 0 103 |
104 | 105 | What’s this App U Thing All About? 106 | http://www.appcelerator.com/blog/2015/04/whats-this-app-u-thing-all-about/ 107 | http://www.appcelerator.com/blog/2015/04/whats-this-app-u-thing-all-about/#comments 108 | Wed, 29 Apr 2015 16:00:49 +0000 109 | 110 | 111 | 112 | 113 | 114 | http://www.appcelerator.com/?p=39878 115 | When we launched 4.0 and Arrow earlier this month and announced a new day for the Platform, we were excited to see what the response would be. ]]> 116 |

When we launched 4.0 and Arrow earlier this month and announced a new day for the Platform, we were excited to see what the response would be. So far we’ve seen over 25,000 beta signups and are looking forward to dispensing with invites and fully opening the doors in the coming days. 117 | 118 |

One of the highlights of our revamped website that we were especially excited to share with you is App U. It’s a curated collection of videos and tutorials, covering everything from cross-platform concepts to detailed examples and code walkthroughs. 119 | 120 |

Appcelerator University
121 | 122 |

The goal in building App U was to enable our users by providing short consumable videos that help you get in, and get on to success with the Platform in no time flat. Fun fact: it was built using our new Arrow capabilities, so you know we’re eating our own dogfood. 123 | 124 |

Here are some of the features to check out: 125 |
    126 |

  • Streamlined Browsing: Access videos by channel or series, so you can find just what you need just when you need it.
  • 127 | 128 |

  • Quick Bites: Easily consumable info on the topics that matter most to the community.
  • 129 | 130 |

  • Event replays: Whether it’s one of our tech talk web-events or a conference presentation, we have a channel where you can find and replay the events you can’t attend live.
  • 131 | 132 |

  • Code samples: to help you learn by doing, there are interaction points in select videos that link to Gists on Github so you can see, download and play with the code being discussed.
  • 133 | 134 |

  • Mobile App: Access App U content with any device and on-the-go (App Store and Play Store).
  • 135 | 136 |
137 | 138 |

Sound good so far? Explore App U for yourself and let us know what you think – we are continuously adding new content and functionality, so there should always be something new to learn! 139 |

]]>
140 | http://www.appcelerator.com/blog/2015/04/whats-this-app-u-thing-all-about/feed/ 141 | 0 142 |
143 | 144 | The Week in Mobile: April 19-25, 2015 145 | http://www.appcelerator.com/blog/2015/04/week-mobile-microsoft-apple-watch/ 146 | http://www.appcelerator.com/blog/2015/04/week-mobile-microsoft-apple-watch/#comments 147 | Mon, 27 Apr 2015 16:00:43 +0000 148 | 149 | 150 | 151 | 152 | http://www.appcelerator.com/?p=39873 153 | Microsoft embraces Apple Watch, Android Wear to untether users from smartphone, Google unleashes “Mobilegeddon” and more.]]> 154 |

Microsoft embraces Apple Watch, Android Wear to untether users from smartphone, Google unleashes “Mobilegeddon” and more


155 | 156 |

Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. This content is designed to inspire you and your company to take advantage of the many benefits mobile can offer.

157 | 158 |

Google Launches Tweak to its Search Engine Algorithm, Sites Cry “Mobilegeddon”

159 | 160 |

Last week, Google launched the latest tweak to its search algorithm. Nicknamed “Mobilegeddon,” the change could have a significant impact on how companies’ websites are ranked in search results on smartphones because the adjustment now favors websites that are more mobile-friendly. 161 | 162 |

Sites that meet Google’s new standards will rank higher in search results. To be considered mobile-friendly, a site must have text that is readable without tapping and zooming, tap targets must be spaced out appropriately and pages must avoid unplayable content or horizontal scrolling. 163 | 164 |

According to recent tests, the update could affect the search ranking for over 40 percent of Fortune 500 websites. As of early April, 46 percent of Fortune 500 companies and 29 percent of the top 500 retail sites were not deemed “mobile-friendly” by Google. However, “Mobilegeddon” will only affect a site’s search ranking on mobile devices. Still, it may be the push companies need to create mobile friendly content.

165 | 166 |

Microsoft Updates OneDrive App with Apple Watch Capabilities

167 | 168 |

Last week, Microsoft updated its iOS cloud storage service app, OneDrive, in order to allow Apple Watch users to flip through photos on their smartwatch. While Microsoft offers a competing smartwatch device, Microsoft Band, it appears the company plans to support Apple’s newest device. OneDrive is the first Microsoft iOS app to support Apple Watch, but it’s likely that other Microsoft apps will see similar updates over the next few months. 169 | 170 |

In the past, Microsoft has demonstrated its commitment to cross-platform functionality with the release of Office for iOS, Outlook for iOS and Android and more. The company offers over 100 iOS and Android apps, so it should come as no surprise that the company is embracing the Apple Watch rather than limiting tools in an attempt to push Microsoft devices.

171 | 172 |

Android Wear to Untether Users From Smartphones

173 | 174 |

A new update scheduled to roll out in the coming weeks will allow Android Wear users to connect their device to a separate Wi-Fi network than the network on which their smartphone is operating. That means users will be able to operate their Android Wear watch on a Wi-Fi network miles away from the Wi-Fi or cellular network connected to their smartphone. 175 | 176 |

The update distinguishes Google’s smartwatch from the soon-to-be-released Apple Watch, which requires users to connect their watch to the same Wi-Fi network as their phone. While the Apple Watch is tightly coupled to its smartphone counterpart, Google’s Android Wear is working to loosen that constraint for its users.

177 | 178 |

Facebook Messenger Making Moves to Replace the Telephone

179 | 180 |

Facebook announced that its Messenger app now makes up 10 percent of global mobile Voice Over IP calls. And CEO Mark Zuckerberg says he expects that percentage to grow rapidly because VOIP can provide higher audio quality than traditional phone calls and is offered for free. Facebook rolled out free mobile VOIP calling to Messenger last April and has already become a viable competitor to apps like Skype who have had a more lengthy presence in the space. Additionally, Facebook launched free VOIP calls for WhatsApp on iOS in mid-April after launching the feature on Android last month. Because Messenger has 600 million users and WhatsApp has 800 million users, it’s possible that the networks are finally large enough to make VOIP calling a viable alternative to traditional phone calls. 181 | 182 |

Last week, Facebook also released its caller ID app, Hello, that links up with the social network platform to identify callers even if they aren’t stored in your phonebook. And, in an attempt to push users to sister apps, Hello makes it easy for users to ignore normal phone calls and use Messenger VOIP to return the call back free. For the time being, the app is built for Android only because iOS doesn’t let apps interact with phone calls. 183 |

]]>
184 | http://www.appcelerator.com/blog/2015/04/week-mobile-microsoft-apple-watch/feed/ 185 | 0 186 |
187 | 188 | 5 Lessons Every Business Can Learn from the World’s Most Tech-Savvy Library 189 | http://www.appcelerator.com/blog/2015/04/5-lessons-queens-library/ 190 | http://www.appcelerator.com/blog/2015/04/5-lessons-queens-library/#comments 191 | Thu, 23 Apr 2015 20:30:48 +0000 192 | 193 | 194 | 195 | 196 | http://www.appcelerator.com/?p=39842 197 | NYC’s Queens Library system is tackling the digital divide with creative mobile solutions—and you can probably learn a thing or two from them.]]> 198 |

NYC’s Queens Library system is tackling the digital divide with creative mobile solutions—and you can probably learn a thing or two from them.

199 | 200 |

Queens Library is one of the world’s most innovative organizations when it comes to technology usage and apps. They’ve won several awards, like the Library of the Future Award, from peers and industry pundits alike, who have recognized their impressive strides. 201 | 202 |

What makes them so impressive? Over the last two years, Queens Library has embarked on a journey to reimagine how mobile can help fulfill their mission of providing learning and enrichment opportunities to their community. This program was jump-started with the gift of over 5,000 tablets from Google after Hurricane Sandy. 203 | 204 |

To take full advantage of this generous gift, Queens Library used the Appcelerator Platform to build a tablet app. With this, library patrons hold in their hands a portal to the library’s entire virtual collection, which includes over 45,000 e-books, 5,000 e-movies and 6 million digital musical recordings, along with 40 databases and e-materials like academic papers and vintage newspaper clippings. Users can also manage their regular library accounts. Queens Library visitors can check out the tablets and take them home, where they are able to work offline and access content even without a Wi-Fi connection. 205 | 206 |

They also offer a “BYOD” (bring your own device) mobile app called Queens Library that patrons can download from the Apple App Store or Google Play and use on their personal devices if they so choose. 207 | 208 |

No other library has put mobile technology to work quite like this, and Queens serves as a model for the wider community, demonstrating how common resources can be delivered in a format better suited to the way people prefer to consume content today. 209 | 210 |

With an IT staff of 56 people, Queens Library has been able to reach 1.5 million users, with 700,000 regularly active. Their apps have processed millions of transactions. 211 | 212 |

It hasn’t been easy, however; they’ve faced serious hurdles in the form of legacy system integration. When you think about all of the disparate data sources and content at any library that would need to be integrated to make a program like this work, it can be dizzying. Backend data sources are a common reason why all kinds of organizations struggle to go mobile, but Queens has managed to succeed using a well thought-out API strategy. 213 | 214 |

By using mobile to its fullest potential, Queens has managed to achieve their goal of bridging the digital divide. They are bringing valuable (yet free) content to a whole community of users, many of whom would not otherwise have easy access to it. The global library community pays close attention to what Queens is up to, eager to see what they do and how they do it—and for good reason. They define the cutting edge in this sector. 215 | 216 |

But there’s plenty Queens can teach the wider business community, too, and really anyone building mobile apps. Here are five lessons other businesses, nonprofits and even indie developers can take away from Queens Library’s mobile initiatives: 217 | 218 |

1. Understand User Context
219 | 220 | Do your users have 24/7/365 access to a reliable internet connection? Doubtful. The Queens Library developers knew that was especially true of their constituents, so they built their tablet app to have offline capabilities. It’s no good for your mobile app to only work under perfect conditions. The key is to understand who your users are and what their reality actually looks like, then build for that context. 221 | 222 |

2. Simplify the User Experience
223 | 224 | You can have the best content in the world, but if the user experience stinks, your users will just go elsewhere. Many companies overcomplicate their apps, but with mobile’s tight form factors, you must make it easy for users to find what they want fast. 225 | 226 |

Queens offers a seamless discovery and access process for content, minimizing the number of steps a patron has to go through to download content. They’ve pulled in resources from tons of different publishers and made it easy for users to get all of the content they want in one place. 227 | With other library vendors, this takes up to 20 steps. Queens can do it in five. 228 | 229 |

3. Take Device Management Seriously
230 | 231 | You might be wondering how Queens Library deals with shrinkage—the inevitable loss of loaned-out tablets. The answer is that they’ve partnered with Airwatch to implement an MDM (mobile device management) system that allows them to manage and secure almost every aspect of the tablet. They can remotely lock them or even erase the content to discourage theft or misuse. 232 | 233 |

What other companies should learn from this? All those things that could go wrong shouldn’t be deterrents from implementing a mobility program. MDM/MAM/EMM solutions are there to help you make sure you have the security, privacy and access controls that you need to make it work. 234 | 235 |

4. Go Beyond Just Building an App
236 | 237 | Even with several very successful apps out there, Queens is hardly resting on their laurels. Instead, they’re looking for opportunities to turn their own success with mobility into a broader business opportunity. They’re exploring licensing their apps to other libraries, building MDM solutions specific to their industry or even offering access to their content to other institutions. 238 | 239 |

They’re also looking for opportunities to achieve other business-related goals, like cutting costs and driving revenue. Businesses of all kinds should look to mobile as not just a new way to interact with customers, but also as a potential avenue to boost the bottom line. 240 | 241 |

5. Think Cross-Platform
242 | 243 | Queens recognized early on that their efforts wouldn’t adequately serve their patrons unless they offered their apps on multiple operating systems. But they didn’t want to waste resources writing and maintaining code for both iOS and Android. Instead, they chose to work with the Appcelerator Platform to provide cross-platform support from a single Javascript codebase. Next, they’re targeting the Windows platform. 244 | 245 |

Using the Queens Library apps and tablets, members of the community are able to not only read books, listen to music and access all kinds of digitized content; they’re also able to get internet in their homes. Queens is successfully closing the digital divide in their borough. One of the amazing use cases they’ve seen is people who are continuing their education via the content they can access through the library’s mobile apps. 246 | 247 |

This public library system, with limited resources and a vast, complex array of legacy data sources has managed to not just “go mobile” but to rethink the way their entire industry operates. They are working hard to bring these innovations to other libraries and education systems, but there’s also plenty that businesses and app makers of all kinds can learn from their inspiring story. 248 | 249 |

How are you rethinking content delivery in the age of mobile?

250 | 251 | ]]>
252 | http://www.appcelerator.com/blog/2015/04/5-lessons-queens-library/feed/ 253 | 0 254 |
255 | 256 | Announcing Box Connectors For Appcelerator Arrow 257 | http://www.appcelerator.com/blog/2015/04/announcing-box-connectors-for-appcelerator-arrow/ 258 | http://www.appcelerator.com/blog/2015/04/announcing-box-connectors-for-appcelerator-arrow/#comments 259 | Wed, 22 Apr 2015 20:02:50 +0000 260 | 261 | 262 | 263 | 264 | 265 | 266 | http://www.appcelerator.com/?p=39823 267 | Today at the Box Dev 2015 conference, held in San Francisco, we announced a key partnership with the storage and file-sharing service. It will bring a connector to Appcelerator Arrow, allowing mobile developers to build apps with open doors to data stored on Box.]]> 268 |

Today at the Box Dev 2015 conference, held in San Francisco, we announced a key partnership with the storage and file-sharing service. It will bring a connector to Appcelerator Arrow, allowing mobile developers to build apps with open doors to data stored on Box. That means any Appcelerator-powered app can now be integrated directly with Box, giving users access to their Box data on their mobile devices without ever having to leave an app. This will arm developers with another tool in their kit to help them deliver rich and engaging mobile experiences.

269 | 270 |

This is huge news for us, but more importantly it’s part of a larger trend in how enterprises are accessing their information. Many of Box’s millions of users work at enterprises that rely on the service to store all sorts of information — private and public, mundane and mission critical. As we move into the post-Web world where mobile’s “always on” paradigm reigns supreme, it’s vital that mobile users have access to all of their information—no matter where they are or what platform they’re using.

271 | 272 |

This partnership is a key part of Box’s push to transform how businesses share, manage and collaborate on the valuable corporate information they store on mobile devices. It’s also a cure for one of the largest challenges faced by mobile developers: connecting to and orchestrating data from a wide range of backend sources.

273 | 274 |

Apps today require data, and lots of it, to deliver high-quality, valuable experiences to users. But it’s not easy for mobile developers to integrate data into apps. That’s the problem that Arrow, our new opinionated framework for building and running mobile-optimized APIs, is helping solve.

275 | 276 |

Box is the latest Arrow connector joining others such as MySQL, Azure, SQL Server, Salesforce and MongoDB. Appcelerator’s customers, partners and developers have already embraced our Arrow Connectors, and we’ll be revealing many more in the near future.

]]>
277 | http://www.appcelerator.com/blog/2015/04/announcing-box-connectors-for-appcelerator-arrow/feed/ 278 | 0 279 |
280 | 281 | The Week in Mobile: April 12-18, 2015 282 | http://www.appcelerator.com/blog/2015/04/the-week-in-mobile-april-12-18-2015/ 283 | http://www.appcelerator.com/blog/2015/04/the-week-in-mobile-april-12-18-2015/#comments 284 | Mon, 20 Apr 2015 16:50:23 +0000 285 | 286 | 287 | 288 | 289 | http://www.appcelerator.com/?p=39802 290 | Apple Watch first day pre-orders estimated at 1 million, Android scrutinized for antitrust activities, Wikipedia looks to boost mobile engagement and more.]]> 291 |

Apple Watch first day pre-orders estimated at 1 million, Android scrutinized for antitrust activities, Wikipedia looks to boost mobile engagement and more


292 | 293 |

Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. This content is designed to inspire you and your company to take advantage of the many benefits mobile can offer.

294 | 295 |

Apple Watch First Day Pre-Orders Almost 1 Million, Apple Releases ResearchKit and More

296 | 297 |

The Apple Watch went up for online pre-order last week, with expected shipping dates ranging from late April to June. While Apple has not announced how many watches were made available or how many orders were placed, online commerce market research firm Slice Intelligence estimates nearly one million orders were placed during last Friday’s pre-order launch day. That’s more devices sold in a single day than the number of Android Wear smartwatches sold in all of 2014. And some analysts are estimating even higher pre-order sales figures for the Apple Watch. Analyst Ming-Chi Kuo estimates over 2.3 million units were ordered globally. Unsurprisingly, estimates state that more than half of those orders were for the less expensive Sport model of Apple’s newest product.

298 | 299 |

In other Apple news, the company has officially opened its ResearchKit framework to researchers and software developers. That means medical professionals will be able to tap into data collected by iPhones, and third-party app developers will be able to create new health-related apps on top of the framework. Apps built on ResearchKit will have access to data from sensors including the accelerometer, gyroscope and microphone, as well as GPS. With the platform now open, consumers are likely to see a myriad of new health- and fitness-related apps hit the app store.

300 | 301 |

Additionally, Apple acquired a new patent last week for a system that uses information from mobile phones to automatically determine whether you’re available for a call and then display that information to contacts. Tapping into contextual information such as your current timezone, ringer setting, battery life, location and network strength, the new technology combines information to determine, for example, if you are in a meeting or some sort of exercise class and unable to take a call. It’s an innovative use of available data and one that will likely get better as time goes on and data analysis becomes more reliable. It is unclear when the new feature will be made available to iPhone users.

302 | 303 |

European Commission Launches Antitrust Investigation into Google’s Android

304 | 305 |

After three years of unofficial examination of Google’s Android operating system, the European commission is finally launching a formal antitrust investigation into Google’s Android platform. The investigation will determine whether Google has initiated anti-competitive agreements with service providers, which may have harmed the development and market access of competing apps and services. Additionally, the commission is investigating whether Google bundles its apps and services on Android devices with other Google apps, services or APIs in order to shut out competition.

306 | 307 |

Following news of the investigation, Cyanogen, a startup building an Android alternative to Google, signed an agreement with Microsoft in which the startup will distribute Microsoft’s software suite. In exchange, Microsoft will design its native apps specifically for Cyanogen’s operating system.

308 | 309 |

Wikipedia Looks to Boost Mobile Engagement with New Social Features

310 | 311 |

Last week, Wikipedia launched a brand-new app for iOS that improves on the original app’s visual design, offers better search functionality and adds social features that allow users to share facts and photos via Facebook, Twitter and Google+. The new app is part of a larger initiative to boost mobile engagement on Wikipedia through updates that improve upon user experience. Wikipedia’s integration into the iOS operating system, which allows users to access content through Spotlight and Siri searches, also means users are less likely to open up a native app when they need to search for information. That’s why the online encyclopedia is stepping outside of its comfort zone to offer social features that may be enough of a hook to attract users to open the Wikipedia app and keep those users engaged for longer periods of time.

312 | 313 |

Push Notifications See Slight Decline in Opt-In Rates

314 | 315 |

On average, users opt in to mobile app push notifications 43 percent of the time, according to a recent study by Urban Airship. While that’s a two percent decline from last year’s opt-in rate, many industries are seeing opt-in rates of over 70 percent. And those who have high opt-in rates report four times the engagement and twice the retention of other apps. Business, charity, travel & transportation and utility & productivity apps saw the highest opt-in rates this year, while mobile gaming, food & drink and retail apps saw the lowest rates.

]]>
316 | http://www.appcelerator.com/blog/2015/04/the-week-in-mobile-april-12-18-2015/feed/ 317 | 0 318 |
319 | 320 | Tweet to Activate: The AppC Community Gets Creative For a Chance at Early Platform Access 321 | http://www.appcelerator.com/blog/2015/04/tweet-to-activate/ 322 | http://www.appcelerator.com/blog/2015/04/tweet-to-activate/#comments 323 | Thu, 16 Apr 2015 18:04:17 +0000 324 | 325 | 326 | 327 | 328 | http://www.appcelerator.com/?p=39737 329 | Earlier this month, we announced Appcelerator Platform 4.0, which includes our latest innovation, Arrow, a slick new opinionated framework for building and running APIs.]]> 330 | 342 | 343 |

Earlier this month, we announced Appcelerator Platform 4.0, which includes our latest innovation, Arrow, a slick new opinionated framework for building and running APIs. With over 15,000 waitlist sign-ups within the first few days, we decided to allow a select few to jump to the top of the list and straight to activation. All you have to do is tweet a creative reason for why you want, need or deserve early activation using the hashtag, #Tweet2Activate.

344 |

We’ve already seen dozens of community members go out on a limb to share why they can’t wait. We are continuing #Tweet2Activate for the next week or two after which we expect to fully open up the new Platform to everyone. 

345 |

If you’re not the patient type and are looking for inspiration, here are a few of our favorite responses thus far – from poetry and GIFs to some tempting offers of food:

346 |

347 | 348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 | 365 | 366 |

Are you ready for early access to 4.0? Tweet using the hashtag #Tweet2Activate and tell us why. 367 | Bonus points for creativity!

368 | ]]>
369 | http://www.appcelerator.com/blog/2015/04/tweet-to-activate/feed/ 370 | 0 371 |
372 | 373 | Xcode 6.3 and iOS SDK 8.3 compatibility 374 | http://www.appcelerator.com/blog/2015/04/xcode-6-3-and-ios-sdk-8-3-compatibility/ 375 | http://www.appcelerator.com/blog/2015/04/xcode-6-3-and-ios-sdk-8-3-compatibility/#comments 376 | Tue, 14 Apr 2015 20:30:42 +0000 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | http://www.appcelerator.com/?p=39694 386 | Last week on April 8 Apple released Xcode 6.3 including the SDK for iOS 8.3. We have tested the current versions of Titanium Studio and Appcelerator Studio as well as our CLIs and current SDK for these new versions and found no problems. The same is true for the beta versions of the upcoming 4.0 GA.]]> 387 |

Last week on April 8 Apple released Xcode 6.3 including the SDK for iOS 8.3. We have tested the current versions of Titanium Studio and Appcelerator Studio as well as our CLIs and current SDK for these new versions and found no problems. The same is true for the beta versions of the upcoming 4.0 GA.

388 | 389 |

If you do run into a problem you believe is caused by an incompatibility issue, please create a ticket on JIRA including as much specifics about the error as you can.

390 | 391 |

The previous release of Xcode and iOS included WatchKit, which we have phase 1 support for in beta. We will have a detailed guide ready at GA.

]]>
392 | http://www.appcelerator.com/blog/2015/04/xcode-6-3-and-ios-sdk-8-3-compatibility/feed/ 393 | 0 394 |
395 | 396 | The Week in Mobile: April 5-11, 2015 397 | http://www.appcelerator.com/blog/2015/04/week-mobile-apple-watch-samsung/ 398 | http://www.appcelerator.com/blog/2015/04/week-mobile-apple-watch-samsung/#comments 399 | Mon, 13 Apr 2015 16:00:36 +0000 400 | 401 | 402 | 403 | 404 | http://www.appcelerator.com/?p=39689 405 | Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. ]]> 406 |

The Apple Watch learning curve, how to develop apps for cars, Samsung patents eye movement tracking technology and more


407 | 408 |

Each week we round up the top news stories, think pieces and other content that centers on the fast-paced, quickly changing world of mobile technology. We tell you which companies are employing clever mobile strategies, illuminate new ways of thinking about mobile and offer a peek at meaningful trends in the industry. This content is designed to inspire you and your company to take advantage of the many benefits mobile can offer.

409 | 410 |

The Apple Watch Comes with a Learning Curve That’s Well Worth the Effort

411 | 412 |

Unlike any other Apple product before it, the Apple Watch comes with a learning curve that may alienate some consumers, says Farhad Manjoo in the New York Times. According to Manjoo, the device is best suited for tech savvy users who are regularly flooded with notifications and are looking for a way to manage their digital lives. Many will need to play with software settings and personalize functionality in order to get the most from their new device.

413 | 414 |

Although it doesn’t exactly come “ready to use,” says Manjoo, those willing to learn how the Watch works and adjust features to fit personal needs will reap some major benefits. When calibrated right, the Apple Watch conveys information in a way that requires far less time and engagement from the user. Because information is abbreviated and notifications come in the form of various vibrations, the user can typically get information in seconds rather than minutes. 415 | 416 |

But, perhaps most exciting, the device promises to change the way we interact with the world around us by becoming a “remote control” for the real world. Apple’s vision for the device could mean your Watch will become the key to your hotel room, the ticket to a concert or a quick connection to an Uber.

417 | 418 |

Developing Apps for Cars May Be Toughest Task Yet

419 | 420 |

Car manufacturers have been using computers to power everything from fuel injectors to brakes, but the systems have historically operated as islands, with little connection with the outside world. Now all that is changing. In an attempt to satisfy customers who value connection, speed and mobility, manufacturers are beginning to increase cars’ connectivity with the world around them. 421 | 422 |

As this type of connection becomes increasingly possible, developers will need to take on what may be their toughest task yet: designing apps that integrate with cars. Challenges include spotty network connections and the complexities of delivering information to users who are operating vehicles. According to Infoworld, among other considerations, developers will need to keep the following in mind:

    423 |
  • Car manufacturers have control over platforms – To gain access to tightly controlled platforms, make sure apps are easy to use and understand and make sense in the context of driving.
  • 424 | 425 |
  • The network is inconsistent – Developers won’t be able to make assumptions about network strength, since it can change from moment to moment in a speeding vehicle. Because user location is constantly changing, developers will need to build apps that work online or off.
  • 426 | 427 |
  • It’s not just about the human anymore – APIs should be designed to handle requests from both humans and algorithms, because apps will likely need to interact with both manual user commands and automated ones originating from the vehicle itself.
  • 428 |
  • 429 | Hands-free is a must – Because users are driving, manufacturers are reluctant to accept apps that could distract from the road. Leave apps that rely on visuals behind and replace them with voice-enabled tools.
  • 430 |
431 | 432 |

Apple’s iPad Lives On Thanks to Extreme Flexibility

433 | 434 |

Apple’s iPad turned five this month. Though it has celebrated years of success, recent sales have been flat. The device may not be flying off the shelf like it once was, but as Darrell Etherington argues in Techcrunch, the iPad still has a full life ahead of it thanks to its extreme flexibility. 435 | 436 |

As Macbook battery life continues to improve and the iPhone 6 boasts a larger screen so users can take on bigger tasks, the iPad is still uniquely suited for certain functionalities. For example, the iPad is the perfect Apple device for reading digital books or acting as an interactive cookbook. The iPad’s size and capabilities make for the perfect travel companion or the ideal wall-mounted technology. What makes the iPad such a success is its ability to become whatever its user needs through the apps it supports, which continue to advance even when the device itself has not.

437 | 438 |

Samsung Patents Eye Movement Tracking Technology

439 | 440 |

Last week, Samsung received a patent for technology that allows users to control their phones or tablets using eye movements. New sensors on devices will soon make it possible to track user eye movement and connect it with commands. For example, a user could control volume within a music app by moving their eyes left to right over the screen. A separate sensor will track blinks, which work like the click of a mouse. The new patent opens up a wide range of possibilities for devices and mobile apps that could transform our devices from being controlled by the tap of a finger to being commanded by the blink of an eye.

]]>
441 | http://www.appcelerator.com/blog/2015/04/week-mobile-apple-watch-samsung/feed/ 442 | 0 443 |
444 |
445 |
446 | 447 | --------------------------------------------------------------------------------