FUNCTIONAL ASPECTS OF GREAT NATIVE MOBILE APPS

Preview:

Citation preview

FUNCTIONAL ASPECTS OFGREAT NATIVE MOBILE APPS

http://www.flickr.com/photos/ourcage/8343799386/

Functionality to go from good to great

• Why create a native mobile app if you aren't going to use the hardware that is hard or impossible to access in a mobile web app?

• Other functional considerations related to creating great apps

• We'll continue the conversation about "great apps" in Unit 5.

Accessing phone hardware

• E.g., Compass, Geolocation, Accelerometer, Shake, Sound, Camera

• Some of these are robustly supported only in certain mobile browsers (e.g., compass in Safari) but available and reliable in native apps

• Others are available in most mobile browsers (e.g., geolocation), but more features are available in native apps

Compass

var win = Ti.UI.createWindow({backgroundColor : "#FFFFFF",title : "test"

});var lbl = Ti.UI.createLabel();win.add(lbl);win.open();

function display(e) {lbl.text = e.heading ? 'compass:' + e.heading.magneticHeading + ',when:' + (new Date()).getTime() : 'null heading';// see also the trueHeading property, used along with location tracking

}

// you can get heading just once with getCurrentHeading() or you can monitor it…Ti.Geolocation.addEventListener("heading", display);// use removeEventListener when done!!

Geolocation

function display(e) { lbl.text = e.coords ?

'lat:' + e.coords.latitude + ',lon:' + e.coords.longitude + ',when:' + (new Date()).getTime() : 'null coords';}

Ti.Geolocation.preferredProvider = Titanium.Geolocation.PROVIDER_GPS;Ti.Geolocation.purpose = "CS496";Ti.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; // can use lower accuracyTi.Geolocation.distanceFilter = 10; // can use broader filter

if (Titanium.Geolocation.locationServicesEnabled === false) {alert('You need to turn GPS on.');

} else {

// to get location just onceTi.Geolocation.getCurrentPosition(display);

// to continually get locationTi.Geolocation.addEventListener('location', display);// unregister with Ti.Geolocation.removeEventListener(display) when done with it!

}

Accelerometer

• When the device isn’t accelerating, it’s more of a “gravity-meter” than an accelerometer

var listener = function(e) { lbl.text = 'accel: ' + e.x + ';' + e.y + ';' + e.z;};

Ti.Accelerometer.addEventListener('update', listener);

Ti.Accelerometer.removeEventListener('update', listener);

Shake

Ti.Gesture.addEventListener('shake', fn);

Ti.Gesture.removeEventListener(fn);

// No API for just retrieving once (obviously?)// Seems to only detect really strong shakes (?)

Key cautions

• Accessing this specialized hardware is very battery-intensive

• Option #1: Just retrieve value once– Use setTimeOut every few minutes if needed

• Option #2: Register for the event listener– Look into the specialized APIs for filtering events

• E.g., "only fire a geolocation event on change >100 meters"

– Unregister your event listener as soon as possible

Camera

var win = Ti.UI.createWindow({backgroundColor : "#FFFFFF",title : "test"

});

var btnCamera = Ti.UI.createButton({title : 'Take picture',top : 20

});

btnCamera.addEventListener('click', function() {Titanium.Media.showCamera({

success : function(event) {var photoTaken = event.media;

if (event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO) {var imgView = Titanium.UI.createImageView({left : 10,width : 300,height : 300,image : photoTaken});

win.add(imgView);}},cancel : function(event) {alert('cancel');},error : function(event) {alert('error');}

});});

win.add(btnCamera);win.open();

Other functional considerations

• Great apps also have functionality for:– Validating inputs, managing state throughout an

application's lifecycle– Taking advantage of relevant specialized

user interface controls and views– Selectively applying platform-specific APIs

Input validation: Assuring that your app's state is initialized from user data correctly

• Sadly, form validation is very underdeveloped in Titanium

• Best approach is to:1. Add labels to your form, for showing error msgs2. In your button click handler, check every input

(e.g., with regular expressions)3. If an input is invalid, set an error message;

else, clear error message(s) and continue.

Handling lifecycle events

• Apps can be paused and then resumed– For example, if user goes to home screen and then

comes back to your app– iOS and Android have slightly different events, but

Titanium hides some of this from you

• On pause, – Save any state that cannot be lost, release resources

• On resume,– Reinitialize from saved state, reinitialize resources

Example of handling lifecycle events

Ti.App.addEventListener('pause',function(e) {// call removeEventListener for hardware// save user data to local storage

});Ti.App.addEventListener('resume',function(e) {

// reload user data from local storage// call addEventListener for hardware

});

You can also detect Android-specific lifecycle events

• Detecting your execution environmentfunction isAndroid() { // iOS vs Android

return Ti.Platform.name == 'android';}

• Accessing Android-specific events (another video will cover the Android lifecycle in detail)

Ti.Android.currentActivity.addEventListener('create', function(e) {// called when the app's current activity (window) is created

});Ti.Android.currentActivity.addEventListener('start', function(e) { … }); Ti.Android.currentActivity.addEventListener('resume', function(e) { … });Ti.Android.currentActivity.addEventListener('pause', function(e) { … });Ti.Android.currentActivity.addEventListener('stop', function(e) { … });

Speaking of which…

• Great apps take selective advantage of functionality that is platform-specific– Yes, this decreases portability– But it has the potential to improve the user

experience and benefits given to users

• And use specialized user interface controls – That might be rendered in platform-specific ways

Judicious use of specialization

• Specialization driven by guidelines & hardware– E.g., guidelines: iOS apps use toolbars to navigate– E.g., hardware: Android phones have 2+ buttons

• FYI, you can also specialize images– Depending on platform and screen density

(essentially dots per inch)– Customize using Resources subdirectories

ScrollViewRendered nearly the same on iOS & Android

var win = Ti.UI.createWindow({backgroundColor : "#FFFFFF",layout : 'vertical'

});

var scrollView = Ti.UI.createScrollView({contentWidth : 'auto',contentHeight : 'auto',showVerticalScrollIndicator : true,height : Ti.UI.FILL,width : Ti.UI.FILL,layout : 'vertical'

});for (var i = 0; i < 100; i++)

scrollView.add(Ti.UI.createLabel({text : "item " + i,color: '#000000'

}));win.add(scrollView);win.open();

PickerSame code, rendered differently on iOS & Android

var win = Ti.UI.createWindow({ backgroundColor: "#FFFFFF", layout: 'vertical'});

var picker = Ti.UI.createPicker({ selectionIndicator: true});

var items = [];items[0]=Ti.UI.createPickerRow({title:'OSU'});items[1]=Ti.UI.createPickerRow({title:'UO'});items[2]=Ti.UI.createPickerRow({title:'UW'});

picker.add(items);

win.add(picker);win.open();

TabGroupSame code, rendered differently on iOS & Android

// From the wizard-generated code…//create module instancevar self = Ti.UI.createTabGroup();

//create app tabsvar win1 = new Window(L('home')),win2 = new Window(L('settings'));

var tab1 = Ti.UI.createTab({title: L('home'),icon: '/images/KS_nav_ui.png',window: win1

});win1.containingTab = tab1;

var tab2 = Ti.UI.createTab({title: L('settings'),icon: '/images/KS_nav_views.png',window: win2

});win2.containingTab = tab2;

self.addTab(tab1);self.addTab(tab2);

You can just use Ti.UI.createWindow(…), FYI

CoverFlowViewOnly available on iOS

var win = Ti.UI.createWindow({backgroundColor : "#FFFFFF"

});

var imgs = [];for (var i = 1; i <= 3; i++)

imgs.push({image : 'img' + i + '.jpg',height : '33%',width : '33%'});

var view = Titanium.UI.iOS.createCoverFlowView({backgroundColor : '#00000',images : imgs,width : Ti.UI.FILL,height : Ti.UI.FILL

});win.add(view);win.open();

http://www.flickr.com/photos/musicbook/3525997685http://www.flickr.com/photos/kbcool/2226493331

http://www.flickr.com/photos/ranh/2390167998

ToolbarOnly available on iOS

var win = Ti.UI.createWindow({ backgroundColor : "#FFFFFF" });

var btnReview = Titanium.UI.createButton({title : 'Review',style : Titanium.UI.iPhone.SystemButtonStyle.DONE,

});// systemButton specifies a standard appearance (has nothing to do with behavior)var btnTrash = Titanium.UI.createButton({

systemButton : Titanium.UI.iPhone.SystemButton.TRASH,});var btnCancel = Titanium.UI.createButton({

systemButton : Titanium.UI.iPhone.SystemButton.CANCEL});var spacer = Titanium.UI.createButton({

systemButton : Titanium.UI.iPhone.SystemButton.FLEXIBLE_SPACE});var toolbar = Titanium.UI.iOS.createToolbar({

items : [btnCancel, spacer, btnTrash, spacer, btnReview],top : 0,borderTop : false,borderBottom : true

});win.add(toolbar);win.open();

MenuOnly available on Android

var win = Ti.UI.createWindow({fullscreen : true

});

var activity = win.activity;

activity.onCreateOptionsMenu = function(e) {var menu = e.menu;for (var i = 0; i < 4; i++) {

var menuItem = menu.add({title : "Choice " + i

});menuItem.addEventListener("click", function(e) {

alert("Your choice has been noted.")});

}};

win.open();