├── 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 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 | 
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 |
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 |
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:
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.