Beruflich Dokumente
Kultur Dokumente
Contents
1 Meet the Appcelerator SDK
2 The basics of Alloy
3 Alloy Widgets
4 Tabs and Advanced Android Customization
5 TableView, third-party modules and working with Local Data
6 ListView, Web Services and Twitter Integration
7 Working with the Camera and native sharing with social networks
8 Top 10 Tips for successful cross-platform development
A Working with JavaScript
B Installing and configuring Appcelerator
C Submitting apps to Google Play and iTunes
Preface
Not long ago, I was involved in building cross-platform desktop applications for the
medical industry. In the process, I learned a lot about cross-platform technology and
frameworks, as well as the psychological and behavioral aspects of building applications
for users of different operating systems.
In 2009 I started building apps with Appcelerator, and in 2010 I started training web
developers on how to build cross-platform mobile apps with it. I applied my crossplatform experience to the training process, and realized it really made a big difference.
This book is the result of these years of developer training and testing of different
methodologies of teaching Appcelerator to new developers.
To understand cross-platform as a concept, we need to properly define it first. Crossplatform doesnt mean to build an application that runs, renders and behaves in exactly the
same way across all platforms - thatll be a website. Building cross-platform applications
actually means that you use the same tool/framework/language, to produce the binary for
the platforms you support.
Platforms are different, and thats by design. Apple and Google need to create very clear
differentiators in their user experience - and if we add Windows Phone to the mix, the
differences are even more drastic. This is part of their strategy of making their platform
unique, increase adoption, and most of all, keep users from switching to other platforms.
If youre an iOS user, and youre given an Android device, youll most likely feel lost and
will want to go back to iOS, because thats what you know - and the same thing the other
way around. Think about it : If iOS and Android apps look and behave exactly the same
on any platform, then whats making users decide between one platform or the other?
The platforms usability guidelines are created to make sure that your app feels right in the
platform. These guidelines are very different across platforms, and your app is designed
around them. This means that knowing and embracing
vii these guidelines is the key to understand how to design a best-in-class mobile app.
Then, understanding how to work with Appcelerator will allow you to implement these
different designs from a single code-base - and thats what cross-platform is.
Can you build an app with a platform-agnostic design? Yes, in fact you can do that with
Appcelerator, as well as with Xcode or Java. Just keep in mind that doing this is a
conscious developer choice, and not something the platform is forcing you to do.
Ive setup the website http://buildmobileapps.io, and Ive created a mailing list. I
encourage you to join the list if you havent done so, as Ill be using it to share
information relevant to this book, such as breaking changes and updates in the installation,
configuration and troubleshooting process.
All the code is hosted on GitHub at http://github.com/ricardoalcocer/ appc_book_code.
For consistency, Ill be using GitHubs built-in issues system to answer questions about
the code. I can also be reached anytime via Twitter as @ricardoalcocer.
Part I
The basics
Chapter 1
UI/UX standpoint using native SDK-provided screen controls and navigation structures.
You dont have to be an experienced JavaScript hacker either. Although well be
implementing advanced functionalities into our apps, this is not a book about complex
programming patterns or JavaScript best practices.
In this chapter youll see how Appcelerator effectively solves native crossplatform
development problems, such as using native user interface components, and how you can
start building native mobile apps using your current web development skills.
device without a developer account. As opposed to web app development, even though
youre using web technologies, with Appcelerator you are in fact a native developer of the
platform, but using a cross-platform framework on top of their native tools.
iOS (iPhone, iPod Touch, iPad) Runs only on Mac
Xcode and iOS SDK (free download)
An Apple developer account is required to test on device and publishing to the AppStore Android (Phones and Tablets)
Runs on Windows, Mac or Linux
Android SDK Manager and desired platform SDKs (free download)
Can test on device without a developer account. A developer account is only needed to publish to the Play Store
code that may fail or slow down the execution of your app. Figure 1.2 shows how Alloy
works as compared with Classic.
Alloy takes your XML, TSS and JavaScript files and generates from them optimized
Appcelerator code, which is then passed over to the regular Appcelerator cycle.
If you run any of these two listings, Classic and Alloy, the result is the same: a native
toggle switch as seen in Figure 1.3.
No need to add or change anything else at this point; youre now ready to test your app.
Click on the Run icon as shown on Figure 1.7 and select your target simulator.
on iOS the simulator will shutdown briefly and will come back up, launching your
updated app. For Android, you should avoid closing the emulator, as Appcelerator will
reuse the same instance and simply reinstall the app onto it. Alternatively, Appcelerator
offers a feature called LiveView, which automatically reinstalls your app when you save
your project.
Congratulations, youve just built your first native, cross-platform app for iOS and
Android using Appcelerator! As you can see, Alloy gives you the benefit of writing an
App using an HTML-like syntax. Notice also that achieving these results from a web app
would have required more lines and code, plus the inclusion of stylesheets and pre-built
JavaScript libraries.
1.3 Summary
With the Appcelerator SDK you get access to the native SDKs straight from your
JavaScript or Alloy code. Each platform SDK contains a host of native UI controls (Text
Boxes, Buttons, Switches, etc.) and navigation structures designed to provide consistent
look-and-feel and app behavior. By using the platform-provided controls, you dont need
to use CSS/JavaScript UI frameworks to emulate the user interface. Understanding and
embracing these entities will ensure that your app always looks and behaves like the
platform its running on, not to mention the amount of time you save by not having to
create your own navigation structures. In chapter 2 well dive into these UI controls, well
learn when to use them, how they render across platforms and how to make cross-platform
UI/UX decisions for your mobile app.
What to remember about the Appcelerator SDK:
With Appcelerator you use JavaScript, a language you already know and that is easier to
learn and master than Java and Objective-C
You can keep a single code-base for your app regardless of how many platforms youre
targeting
You use the Web Development paradigm of a markup language, stylesheets and
JavaScript and obtain native apps
You can train your development team in a fraction of the time required to learn the native
platform tools
You can dramatically speed up your time-to-market and end up with a cross-platform app
that is easy to maintain and scale
Chapter 2
Figure 2.1:
Relationship between the platform SDKs, the Titanium API and Alloy
Think of it this way: You could bake a cake gathering all your ingredients one by one,
baking powder, flour, etc., or you can buy a mix to which you only need to add water. This
mix is your framework for baking cakes. Alloy does this for Titanium.
Alloy code is much easier to organize because the concept of MVC is to keep UI code in
one side, styling in another, data models in another, and the code 2.1. What is Alloy?
that binds the UI to the logic of your app in another. The result is source code that is easier
to write, maintain and scale.
To make it friendlier to web developers, Alloy uses a declarative markup language to build
the user interfaces, that is very similar to HTML. Styling is applied using stylesheets
(called Titanium Style Sheets or TSS) similar to CSS, and then uses pure JavaScript to
implement functionalities using the Titanium API. In the following section, Ill give you
an introduction of how the Titanium API is laid out, and how to use it in your mobile apps.
At the JavaScript level, Titanium functions are part of a high-level object called Titanium.
Figure 2.2 shows the namespaces available in the Titanium API. Inside the top-level
object are different sub-objects called namespaces. For example inside the UI (User
Interface) namespace youll find functions to implement user interface objects.
In this other example the variable loginButton contains a Button object with Login as its
title. Other namespaces are Media, Map, Network, Filesystem, and many others, but you
dont have to memorize them all. As you start developing apps youll see yourself needing
a particular functionality and finding the reference in the official documentation.
The Titanium API is the set of objects and functions that expose to JavaScript the
functions of each platform SDKs. Since Alloy is a framework on top of the Titanium API,
you still need to have a clear understanding of how the API is laid out. Later in this
chapter youll see in context how these namespaces relate to Alloy and how to use them in
your own apps.
To understand what MVC is, why its important and how Titanium implements it, lets
first take a deeper look at Titanium Classic, which is non-MVC code, on Figure 2.3.
Window is a styling property and the handler for the click implements the behavior of the
button.
As a web developer you see this all the time with styles defined inline within the HTML.
It is considered a very bad practice; all styles should be specified as stylesheets. If you
need to make changes to the UI, you make them from the outside, and oftentimes you
dont have to touch the HTML at all.
This doesnt necessarily mean that Titanium Classic is not good - far from it, but in order
to follow best coding practices, developers ended up creating their own ways of separating
UI, styling and behavioral code.
MVC is not a new concept, nor is it exclusive to mobile development or Titanium. MVC
has become a de-facto standard in building applications of all types and for different
programming languages and modern software developers often look for frameworks that
implement the MVC paradigm because of its many advantages. Alloy represents
Appcelerators official response to this question.
Description
Alloy views follow the same paradigm used for web development, and are composed of a combination of two files: XML and TSS. The XML file is
used to specify the components of your screen, such as buttons, text fields and images. The TSS (Titanium Style sheet) file works together with the
XML file to provide styling to the components of your screen.
The Alloy controller is a JavaScript file that has access to your XML view. From this JavaScript file you can access the components of the screen,
assign and respond to events and write your screens programming logic.
Alloy models use Backbone.js, a popular JavaScript library. Backbone provides you with a user-friendly programming interface to create local data
stores.
Alloy Views consist of two files: the XML that defines the contents of the screen and the
TSS that defines the styling. The XML file always starts with the <Alloy >tag and ends
with </Alloy >, and between these tags you place the user interface components required
for your window.
In simple terms, a View is used to hold something that will appear on the screen
something that will be seen by the end user. The most common case is to use a View to
represent a Window, in which case your <Alloy >tag will be followed by a <Window >tag,
and inside this <Window >tag youd add your user interface elements.
View files are not exclusively used for Windows. As youll see in future chapters, you can
use View files to represent chunks of code that will be reused across other views, or to
hold templates for list rows that will be repeated inside a loop in your controller code.
Lets look at a side-by-side example of Titanium Classic and Alloy in Figure 2.6.
Figure 2.6:
Titanium Classic and Alloy side-by-side
In the image above you can see that Alloys XML is much more readable and easier to
understand than its pure JavaScript counterpart. Also notice that were only defining that
these components exist and all styling will be added to an external TSS file.
Alloy tags are very similar to HTML tags and in Figure 2.7 you can see the components of
a typical tag. Note that the height and width properties are included only to illustrate that
they are allowed within the tag. As mentioned earlier, it is a bad practice to couple UI and
Styling code. In section 2.2.3 youll see how to use TSS files to provide styling properties.
Figure 2.7:
Anatomy of the XML tag
Titanium has a user interface component called View located at Ti.UI.View. This
component is not to be confused with the Alloy View we saw earlier. The View in Alloy
refers to the View in Model-View-Controller, while the former refers to an actual user
interface object very similar to the DIV in HTML
In Figure 2.7 above we use the <Label >tag. These tags, although they look like HTML,
are actually representations of the Titanium API living underneath. For the Label tag,
there is a Label object in the Titanium UI namespace, and the tag is bound to the
createLabel method described at http://docs.
appcelerator.com/titanium/latest/#!/api/Titanium.UI.Label.
All Alloy XML tags map directly to the Titanium API, which is why you should be
familiar with the underlying Titanium factory methods. Earlier we saw
Ti.UI.createWindow and Ti.UI.createButton. To know the corresponding Alloy XML tag,
simply remove Ti.UI.create and the remainder is your XML tag, that is <Window >and
<Button >starting with a capital letter, and naturally, just like in HTML, all tags require a
Lets now take a look at the second component of the Alloy View, the TSS file. Titanium
Stylesheets (TSS) are very similar to CSS in function, but a little bit different in structure
as seen in Figure 2.8.
Figure 2.8:
From Titanium Stylesheets you have access to the properties of the objects provided by
the Titanium API. The concept is very similar to CSS, but remember, youre not using the
DOM.
At first youll see yourself going back and forth to the API documentation to understand
which properties are available for each object, but in no time youll realize that most
object share the same property names, so theres actually very little youll have to
memorize. In the coming chapters well see how TSS files can be separated as themes and
how they can even have conditional operations within them.
The controller is where you add your actual JavaScript code. Up to this point youve
created a Window and have applied styling properties. But your screen is completely
static. The controller file is the place where you make your screen do something useful.
From your controller youll have automatic access to all your screen components. Our
previous example defined a button inside the screen:
< Button id = mybutton class = button onClick = doclick > Click Me </ Button >
The doclick function is a simple JavaScript function that is calling JavaScript alert
function to show a message on the screen. Notice that this function has one argument, evt.
The name is arbitrary, but it stands for event and it simply means that this function will
receive an event object, containing information about the object that generated the click
event, in this case the Button. This is really not that much different to the way you build
JavaScript function for the browser.
Theres still one little detail we need to address, and that is that since this is our first (and
only) window, we need to explicitly open it. But the Window was defined in XML. How
can we get access to the XML from the JS file? Well, Alloy has a built-in object called $
and it provides direct access to the XML file as shown in Figure 2.9. In fact, when reading
Alloy code with a $, it could be read as this view.
The last component in the MVC pattern is the Model. The Model, short for data models, is
where you define structured databases for your app. Alloy models are implemented using
Backbone.js, a popular open source JavaScript library that gives structure to web
applications providing features like data collections and custom events.
Alloy can provide you with easy access to local databases as well as establishing complex
connections to web services and perform data synchronizations between the phone and
remote data sources. Not all apps use data models and although Backbone is part of Alloy,
you could still establish data connections without using Backbone. For the time being, you
just need to know that its there, but we wont need it for our example. In this book well
not get too deep into Backbone as it is a complex topic that requires a longer discussion.
Well however get into a very simple implementation in Chapter 6. For more information
about Backbone visit http://backbonejs.org/ or the official Alloy Models documentation at
http: //docs.appcelerator.com/titanium/latest/#!/guide/Alloy_Models.
purpose MP3 player. Mobile devices come with MP3 players, but the one well be
building will include the tracks itll play. Please dont underestimate this example. Ive
used this code virtually unchanged for several projects. What is this good for? Think about
an artist that would like to release a couple new songs through an app; or think about
guided tours in a museum or national park. The possibilities are endless.
Figure 2.12: Differences between the iOS Title Bar and Androids ActionBar Heres where
knowing how iOS and Android work and how Titanium gives you access to native
controls come in handy. Android windows have a component called ActionBar, which is
consistent across all windows in your app, and across all Android apps. On the other hand,
iOS has a Toolbar control. They both serve the same purpose, but notice how on iOS the
title is centered across the Window, while on Android it includes the App icon and the text
is aligned to the left. Dont try to make the two apps look exactly the same, dont try to
remove the icon on the Android side and dont try to have the title centered in both
versions. This is the way the operating system works and as you start using native controls
and embracing their behavior, youll realize how much time youre saving by not having
to build controls yourself. Besides, by using these conventions, your app will look and
behave as any other native app and in the end itll be easier to adopt by your users.
From the functional standpoint we need to know that our app will:
Play MP3 music bundled with the app
The name of the song currently being played will be displayed in the center of the screen
The Play button needs to change to Stop when playing and to Play when stopped
Pressing Next will move you to the next song unless youve reached the end, in which
case it will do nothing
Pressing Previous will move you to the previous song unless youve reached the
beginning in which case it will do nothing
Having made functional and cross-platform UI decisions, we are now ready to start
building this apps user interface. Well divide the development of this app into two
milestones, the first one for building the UI and the second one to implement the
functionalities.
Figure 2.13:
Creating a new mobile project
If youd rather import the code from the companion code package, Figure 2.14 shows you
the steps.
Figure 2.14:
Importing projects into Studio
Lets start by looking at the user interface components well need for this project,
described in Table 2.2. Its always a good idea to create an inventory of required user
interface elements before starting any project.
Component Window
Toolbar
ActionBar
View
Label
ImageView Description
Main app container
Placed on top of the window on the iOS version.
Placed on top of the window on the Android version. The ActionBar will be added on the onOpen event of the Window. Used as an object container.
Similar to the Div in Web Development. Can be of a fixed size or stretched to fit a certain area. Well use it to create the bottom area holding the
control buttons.
Used to add text to the window. Well use it to add the title of the Window and the title of the song currently playing. Used to add images to the
window. Well use it to create the actual previous, play, stop and next buttons, and well use the onTouchStart and onTouchEnd events to give the
button down and button up effect.
Table 2.2: User interface Components required for the MP3 Player
Looking at this UI as a web page we can establish the strategy for dividing our screen. In
Figure 2.15 you can see that well divide our screen into three sections.
Top: On the top well place the Toolbar or ActionBar.
Center: The remainder area well call content and thats where well place the title of the
track currently playing.
Bottom: The bottom section well call Controls Bar and thats where well place our
media player controls.
In the code sample above we have our basic skeleton. Weve also added some special
attributes and components:
The onOpen event specifies the function to run when the Window is actually opened.
This doopen function will be defined in our Controller (JS File)
We used a special Alloy attribute called platform. In this attribute you add the
platforms to which you want this tag to apply. By default all Alloy tags will take effect on
all target platforms. As mentioned earlier, the Toolbar component is an iOS-only element,
so we need to specify that itll be only considered for this platform, otherwise the Android
build process will complain of not knowing what a Toolbar is. Well also be using this
technique inside our TSS and JS files, and its one of Alloys most powerful features,
designed to allow you to use a single code-base to achieve platform-specific
functionalities.
The Toolbar needs to contain a sub-tag called Items, to which we add the user interface
elements we want it to contain. For this project we only want a Label, but to have it
centralized, we add the special FlexSpace on each side of the label, causing it to be
automatically centered.
The FlexSpace is a special iOS button type that will create a flexible/stretchable
transparent area, used to position toolbar buttons and to easier create automatic layouts.
The Android ActionBar
Before we continue, lets do some basic ActionBar setup. Android apps show the
ActionBar by default, but its shown using a dark theme where the ActionBar is black. For
our app we need to use the Android light theme. Ill explain this in details in Chapter 4
when we get into ActionBar Styles, but for the moment go to your apps root folder (the
one with tiapp.xml) and create the folder structure platform/android/res/values. Inside this
folder create a file name builtin themes.xml and add the following contents.
<? xml version =1.0 encoding = utf -8? >
< resources xmlns : android = http :// schemas . android . com / apk / res / android >
< style name = LightDarkBar parent = Theme . AppCompat . Light .
DarkActionBar / >
< style name = Light parent = Theme . AppCompat . Light / > < style name = Dark parent = Theme . AppCompat / >
</ resources >
This code tells Android that you want to have access to some built-in themes, in this case
defined as LightDarkBar, Light and Dark. Then go to your tiapp.xml and add the
following to the android section.
< manifest >
< application android : theme = @style / Light / >
</ manifest >
The creation of the builtin themes.xml file and its content is not Titaniumspecific, but part
of Android native development. If you know how to build Android apps, then this works
exactly as if you were using it from Java.
Here youre telling your app which theme you want to use, in this case Light. Thats all
you need to know about the ActionBar for the moment, but as I said, well get back to it in
Chapter 4.
Lets now continue by working with the button strip that will be displayed on the bottom
of the screen. To build this, well use our web development knowledge. Figure 2.16 shows
the skeleton of this buttons area.
Figure 2.16:
Strategy for building the buttons strip
To build the controls button we use essentially the same approach youd use for a web
page. In HTML you have the Div, a UI component that serves as a visible or invisible
frame that can hold other objects. The control analogous to the Div is the View. Well use
one view that well call controlsbar thatll represent the container for the buttons, and
well assign a class name of controlsbar, as seen in Figure 2.17.
Lets now start writing the styling in the TSS file. As explained earlier, the
TSS file is similar to a CSS file, but the syntax is like a JSON object, because
the file is essentially a JSON file. Our first entry will be to style our container
onOpen = doopen > platform = ios >
class, which corresponds to our Window. Just like in the XML file where we used the
platform attribute to provide platform-specific components, well do the same to provide
two different sets of properties, one for iOS and one for Android.
. container [ platform = ios ]: {
backgroundColor : white ,
top :20 ,
statusBarStyle : Titanium . UI . iPhone . StatusBar . LIGHT_CONTENT , layout : vertical ,
orientationModes : [ Ti . UI . PORTRAIT , Ti . UI . UPSIDE_PORTRAIT ]
}
. container [ platform = android ]: { backgroundColor : white ,
layout : vertical ,
orientationModes : [ Ti . UI . PORTRAIT ]
}
In the code above were taking care of several important details. BackgroundColor is selfexplanatory. We are assigning a top value of 20 for iOS so our window starts exactly after
the statusbar, otherwise the window will start at the top, falling under the statusbar. Lets
look one by one at the rest set of properties in Table 2.3.
Property Description
statusBarStyle iOS property to control the look and
feel of the status bar on the top of
Our next class is the content area, to which well assign a background image.
This will be located in the app/assets folder of your project. When Alloy compiles
your app, this assets folder will become your root folder, so to refer to the image
goldengate.png, we use /goldengage.png and not /assets/goldengate.png.
. content :{
backgroundImage : / goldengate . png
}
The left and right properties with a value of 10 are used to provide a padding of 10 pixels
on each side. Since songtitle is a child of container, which has the default absolute
positioning, the songtitle label object will automatically be centered across the screen.
Finally we assign a white color and set the text within the label to be centered.
The next group of classes and ids will be used for styling the controls of the MP3 player.
. controlsbar :{
bottom :0 ,
height :70 ,
backgroundColor :# cacaca ,
opacity :.5
}
. button :{
width : 33% , height : 50
}
# prev :{ left : 0
}
# play :{}
# next :{ right : 0
}
Thats all for the user interface. If youre curious at this point, you could run the project,
but before you do that, youll need to open up index.js and create placeholders for the
functions that weve referenced in Index.xml. Listing 2.2 shows Index.js with the required
placeholders, which well fill out in the next section.
Listing 2.2: index.js with function placeholders function doopen ( evt ){
}
function prevdown ( evt ){
}
function prevup ( evt ){
}
function playdown ( evt ){
}
function playup ( evt ){
}
function nextdown ( evt ){
}
function nextup ( evt ){
}
$. index . open () ;
In the previous listing we are simply creating empty functions. There is no problem in
leaving them empty; at this point Alloy only needs to know they exist. Run the project and
you should see the user interface as it is so far.
In this section we built a full user interface in XML and applied the corresponding
stylesheets using the TSS file. Dont forget that the whole concept is to build your user
interface from the same source code even if its going to be rendered differently. Know the
target platforms, how they behave and what the user expects from the app, then use
conditional attributes to separate your UI and styling depending on the platform.
In the code above we first create an array with all our MP3 files. This array consists of
JavaScript objects containing the file name, which will be used for the actual Sound Player
In this function we first take care of assigning the title of the Window. Since we are using
an ActionBar on Android and a Toolbar on iOS we need to write conditional code. Just
like the platform property in our XML and TSS files, Alloy has special variables for
platform-specific operations inside controller. In this case were using OS ANDROID to
make sure the Android-specific code is not run under iOS.
In the case of iOS we simply use the syntax $.windowtitle.text=winTitle, as explained
earlier, to get access to the label in the XML file with id = windowtitle. NOTE
This book focuses on iOS and Android, so well only be using the OS ANDROID and OS
IOS conditional variables. However, keep in mind that you could also use OS
BLACKBERRY and OS MOBILEWEB if your app is targeting either of these platforms.
Now lets take care of the placeholder functions we created in the previous milestone.
function prevdown ( evt ){
evt . source . opacity =.5;
}
function prevup ( evt ){ evt . source . opacity =1; moveback () ;
}
function playdown ( evt ){ evt . source . opacity =.5;
}
function playup ( evt ){
evt . source . opacity =1;
if (! audioPlayer . isPlaying () ){
evt . source . image = / btnstop . png playsong () ; # F
} else {
evt . source . image = / btnplay . png stopplayer () ;
}
}
function nextdown ( evt ){ evt . source . opacity =.5;
}
function nextup ( evt ){ evt . source . opacity =1; moveforward () ;
}
When we defined the View in index.xml we assigned two events to each button:
onTouchStart and onTouchEnd. These events are handled here by the functions with
names ending on up and down. As mentioned earlier, functions for screen events receive
an event object, which we are calling evt. The down functions essentially change the
opacity of the image to make it slightly transparent, providing the user with visual
feedback when the buttons are pressed. We do this by calling evt.source and itll give us a
reference to the object that generated the event and access to its properties. The up
function is called as soon as the touch ends (when the finger releases the button). Here we
set the images back to their normal state of opacity=1, and then execute the actual function
for this button.
The case of the Play button is different because the button has two possible states: Play or
Stop. Well use the isPlaying function provided by the Sound Player. If the player is not
playing (!audioPlayer.isPlaying()), it means that we must set it to play, so we change the
image to the stop image.
Lets now implement the functions that will actually play, stop and move around the
songs.
function moveback () {
if ( currentSong > 0) {
currentSong - -;
if ( audioPlayer . isPlaying () ) {
stopplayer () ;
playsong () ;
}
updateScreen () ;
}
}
function playsong ( evt ) {
audioPlayer . url = songs [ currentSong ]. filename ; audioPlayer . play () ;
}
function stopplayer () { audioPlayer . stop () ; audioPlayer . release () ;
}
function moveforward () {
if ( currentSong < songs . length -1) { currentSong ++;
if ( audioPlayer . isPlaying () ) {
stopplayer () ;
playsong () ;
}
updateScreen () ;
}
}
The only thing left to do is to create the updateScreen function and open the window.
function updateScreen () {
$. songtitle . text = songs [ currentSong ]. filetitle ;
}
$. index . open () ;
There we have it: a fully working, cross-platform native MP3 Player. In this section we
built a cross-platform MP3 player using 100% native
controls. In our XML file we used conditional attributes to display the Toolbar control
only on iOS, while for Android we set an open event on the window and took care of
configuring the ActionBar in our controller. We also saw how conditional operators can be
added to stylesheets and used to specify different styling properties to our window. Inside
our controller we assigned all the actual functionality, from establishing the songs to play,
to handling button clicks and playing songs with Titaniums cross-platform sound player.
Even when were merely scratching the surface, you can see with this small project how
Alloy allows you to use a web development paradigm to achieve native user interface,
performance and functionalities from a single code-base.
Figure 2.23:
Alloys position in the Titanium development cycle
When the pre-compilation starts, Alloy reads this file, applies any settings and then
continues with the compilation process. One of these settings is the appss theme. By
default the config.json file is a template with no values.
{
global : {} , env : development : {} , env : test : {} , env : production : {} , os : android : {} , os : blackberry : {} , os : ios : {} , os
: mobileweb : {} ,
dependencies : {} }
Themes are added as a property inside the global object, so you simply add the theme to it.
{
global : {
theme : goldengate },
env : development : {} , env : test : {} ,
env : production : {} , os : android : {} ,
os : blackberry : {} , os : ios : {} ,
os : mobileweb : {} , dependencies : {}
}
By setting this value youve instructed Alloy to look for the styles and asset folder inside
the goldengate folder.
Config.json
The Config.json file provides a Global object, which besides being used to configure your
apps theme, it can be used to store any value you want to change from the outside of your
app. Moreover, it gives you a way to set said values with the granularity required by
modern apps.
{
global : { host : http :// defaultserver . com }, env : development : { host : http :// developmentserver . com }, env : test : { host
: http :// testserver . com }, env : production : { host : http :// productionserver . com }, os : ios : { host : http :// appleserver . com
},
os : android : {
host : http :// googleserver . com },
dependencies : {}
}
The values for this file are available to you by code by using the Alloy object Alloy.CFG.
In the example above the value for host will be available to your app as Alloy.CFG.host.
This value will be different depending on your deploy target. That is, if you compile your
app for iOS, Alloy.CFG.host will hold http://appleserver.com, and if you compile it for
Android it will hold http://googleserver.com. The dependencies property is used to specify
Widgets used by your app, explain in Chapter 4. The values prepended by env: will be
accessible depending on a flag named deploy-type, which you set via the Titanium CLI
and not accessible from Titanium Studio. For more information about the Titanium CLI
visit http://docs.appcelerator.com/titanium/3.0/#!/guide/ Titanium_CommandLine_Interface_Reference-section-35619828_ TitaniumCommandLineInterfaceReference-Build and for more information about the Alloy CFG property
you can visit http: //docs.appcelerator.com/titanium/3.0/#!/api/Alloy-property-CFG
You can now run the app and youll see that the app is exactly as before, only now the
assets and styles are being grabbed from the /themes/goldengate folder, as instructed by
the theme property in config.json, in other words, youve effectively taken your styles and
assets and have placed them in its own container called goldengate. From there on out, the
customizing of your app is extremely easy, as itll only require creating a new theme and
applying the required customizations. Lets go ahead and create a new theme.
Figure 2.26:
Change backgroundImage in content class.
After doing this your app will use a background image named background.png regardless
of the theme youre using. Your theme is now generic and creating new themes will only
require you to provide different versions of the already existing and referenced images.
You could now open config.json and change the theme property to reflect the caribbean
theme, and youll see that the app has a different background. We still need to change
some colors and the position of the control bar, but this is easy because we dont have to
touch the code at all; everything will be done by simply changing graphical assets and
making some minor changes in the index.tss file.
Now open up /themes/caribbean/styles/index.tss, which is the stylesheet for the caribbean
theme. To put the controls bar on the top and change its background color, simply make
the changes shown in Figure 2.27.
Figure 2.27:
Change color and position of the controlsbar class
In Figure 2.27 were changing the controlsbar class to be positioned in top=0 instead of
bottom=0. Also were changing the gray color of #cacaca to the blue #255F89. Finally to
change the actual buttons, replace the files btnnext.png, btnpause.png, btnplay.png,
btnprev.png and btnstop.png located in /themes/Caribbean/assets for new images.
This is all you need to do. The power of Alloy themes have provided you with a way of
changing the complete look-and-feel of your app without actually touching a single line of
JavaScript code.
Now that you have created the new them, its just a matter of opening up your config.json
file and apply the theme:
global : {
theme : caribbean
}
After making this change, save the config.json file, run your app and youll see the app
rendered with the new theme.
2.6. Summary
2.6 Summary
In this chapter youve seen:
The MVC pattern, how it works for the web and how it works with Alloy
Chapter 3
Alloy Widgets
This chapter covers:
Creating Alloy Widgets from scratch
Understanding the Widget file structure
Creating Alloy Widgets out of functionalities of an existing app
In chapter 2 we built an MP3 player and later we implemented Alloy themes to provide an
easy way of changing the look-and-feel of our app, without touching the JavaScript code.
In this chapter well explore Alloy Widgets. A Widget is a user-defined component that
can be added to an app. Each Widget can contain user interface elements along with new
functionality that is not already provided by Titanium.
For example, lets say you want to display photos in a grid format. You could write the
code using ImageViews with the techniques we saw in Chapter 2, but what if you need to
use this grid display in different parts of your app? The traditional way of going around
this is by simply copying and pasting the blocks of code onto different Alloy controllers.
The main problem with this approach is maintainability: if you need to make a change,
youll then have to go through your apps code to manually make the change.
With Alloy, you could build this grid display as a Widget, which will contain all the user
interface elements along with the required functionality. This Widget will get added to
your project as a dependency only once and when needed, you simply add it to your Alloy
view.
In this chapter well see how to create a simple Widget and well explore all its
components. Then, well go back to the MP3 Player app and well build the media player
functionalities as an Alloy Widget, effectively detaching all the media player code from
your main apps code.
with other developers. After you hit OK, Titanium Studio will open for you the
widget.json file and have created a folder structure. Lets see what Alloy built for us.
Studio opens this file for you because its important that you dedicate a minute or two to
it. Youre building this widget now, so it is now that you have fresh on your mind what
this widget is for, plus other details, as seen in Table 6.1.
Property Id
Name
Description
Author
Version
Copyright
License
Min-alloy-version
Min-titanium-version
Tags
Platforms Description
Prefilled by Alloy. Holds the id of your Widget. Prefilled by Alloy with the same contents of the Id field. You should change this value to hold a more
humanfriendly name.
Here you should write the description of what youre widget is for. Think about what youd want to read if you go back to this widget 1 year from
today.
Your name
Version number of this widget.
Copyright. At the very least change the year to the current year.
License to use for this widget. Comes pre-filled with Public Domain. However, if youd like people to feel compelled to contribute code back to you, I
suggest you use MIT License. This is the one I always use: http://alco.mitlicense.org.
The minimum version of Alloy that will compile this widget. If you dont know, enter the version of Alloy youre currently using. The minimum
version of Titanium that will compile this app. If you dont know, enter the version of Titanium youre currently using. Tags that could lead people to
find your widget. A comma-separated list of the platforms this widget is designed for.
Table 3.1: Contents of the Widget.json file Now that you have added the meta-data for
your Widget, lets look at the folder structure, shown on Figure 3.3.
Notice that just like your app views, Alloy XML files must start and end with the Alloy
tag. In this case, we have a Label between these tags. The default TSS file contains the
following:
Label : {
color : #000 ,
font : {
fontSize : 18 ,
fontWeight : bold },
height : Ti . UI . SIZE ,
width : Ti . UI . SIZE }
The TSS code above is the styling corresponding to the Label defined in widget.xml. This
styling is for the widget and the widget only, and will not conflict or overwrite the Label
styling in the app using the widget. Figure 3.4 shows the XML and TSS side-by-side.
Figure 3.5: Location of widget local assets and styles as compared with global ones
Finally, if you open widget.js youll see that the file is empty. The reason for this is
because our basic/default widget has no functionality, as it only shows a Label. Now, lets
see how to use this widget.
The second step, where you tell the app that you want to use this widget, takes place in the
config.json file we saw in the previous chapter, which is located in /app. The last property
in this file is dependencies. This property holds list of widgets to be used by your app.
For each widget you have to specify its name, which is the name of the folder inside
widgets (and the id specified in widget.json), and the version to use (or an asterisk to
specify that you dont care about the version), which you specified in widget.json and was
described in Table 3.1. Titanium Studio does this step for you every time you create a
widget using the New Widget Wizard described above. For all other widget youll have
to open the file and add the entry by hand. At this point your config.json file looks like
this.
{
global : {} ,
env : development : {} ,
env : test : {} ,
env : production : {} ,
os : android : {} ,
os : blackberry : {} ,
os : ios : {} ,
os : mobileweb : {} ,
dependencies : {
com . companyname . widgetid : 1.0 }
}
Make sure you pay special attention to the versioning of your widget. The basic concept
behind Widgets is reusability, so by definition youll have multiple apps using the same
code. As you make changes to your widget you should increment its version number.
Also, you could create a README.md file inside your main widget folder and use it to
write the features of this particular version.
Now that your app knows youre using a widget, and your widget is properly stored inside
the widgets folder, were ready to use in in our app views. We do this using the special
Alloy Widget tag.
The <Widget >tag has two attributes:
src : Specifies the name of the widget id to use
< Widget src = com . companyname . widgetid id = myfirstwidget / >
id : A name you want to give to this widget in order to reference it by code. This Id must
be unique across your view.
To insert this Widget into your current app, open up your index.xml file and add the
Widget tag to it.
< Alloy >
< Window class = container >
< Widget src = com . companyname . widgetid id = widgetid / > </ Window >
</ Alloy >
Once youve added the widget tag to your view, save everything and run your app. The
results will your widget shown in the center of the screen as shown in Figure 3.6.
At the time of this writing, Gitt.io is the de-facto package manager for Appcelerator.
Github : Many people build widgets for their own projects and upload them to Github.
Theyre not that easy to find but theres many.
TitaniumControls.com : Similar to Gitt.io but manually curated with actual screenshots
of the Widget in use.
Figure 3.10:
Transferring view code from app to Widget
While at this, lets also go ahead and add the Widget tag to index.xml, leaving us with the
code in listing 3.1.
Listing 3.1: Index.xml
< Alloy >
< Window class = container
< Toolbar class = toolbar < Items >
< FlexSpace / >
< Label id = windowtitle / >
< FlexSpace / >
</ Items >
</ Toolbar >
< View class = content >
< Label id = songtitle class = songtitle > </ Label > < Widget src = com . alcoapps . musicplayer id = musicplayer / > </ View >
</ Window >
</ Alloy >
onOpen = doopen > platform = ios >
As you can see in listing 3.1 we are assigning musicplayer as the id for our Widget. Later
on youll see that in our controller (index.js) well be able to speak to the Widget using
the syntax $.musicplayer, just as any Titanium built-in UI component.
Going back to the code we moved, illustrated in Figure 3.10, youll see that we didnt only
move user interface elements, but also styling (referenced via Ids and Classes), and
behavior (referenced via onTouchStart and onTouchEnd events). We also need to move the
styling from the host app into the Widget.
To transfer the styling from the app to the widget, open index.tss and locate the styles that
are being used by the code we just transferred. To know exactly what to move, simply
identify anything that is either a class or an Id, as seen in Figure 3.11.
Figure 3.11:
Identifying classes and ids
Cut the code block that contains these classes and Ids from index.tss and transfer them to
Figure 3.12:
Transferring style code from app to Widget
The WPath Function
You may have noticed in Figure 3.12 that .btnprev, .btnplay and .btnnext are all
referencing images with absolute paths. This means that Titanium will look for these
images in the /app/assets, which is the root folder of your app. But we dont want that
because this is a Widget, and the Widget should use its own assets. To fix this, Alloy
provides a built-in function called WPATH. The WPATH function receives a name of an
asset, and simply returns the correct path in the context of this widget. The WPATH
function can be used from style sheets and controllers, and as a rule of thumb, every time
youre referring to an asset from within a Widget, make sure you use it to get the correct
location. Go ahead and change these three references to use the WPATH function as
shown below.
. btnprev :{
image : WPATH ( btnprev . png )
}
. btnplay :{
image : WPATH ( btnplay . png )
}
. btnnext :{
image : WPATH ( btnnext . png )
}
This takes care of the user interface and styling. Next up, lets look at the steps required to
move the functionality of our app onto the Widget.
Figure 3.13:
Methods that belong to the Widget
All these functions are currently in index.js, but should belong inside the Widget. From
index.js we want to specify the songs to play. We also have to build a mechanism for the
Widget to write back to the apps screen. As you remember, our MP3 Player has a label in
the center of the screen displaying the song being currently played. This label will not be
part of the Widget, so we need to make sure the Widget can update that label as the player
moves through songs. Before we move the code to the Widget, lets have a look at listing
3.2 showing how much cleaner our apps index.js will look. Listing 3.2: index.js
$. musicplayer . setSongs ([
{ filename : / songs / file1 . mp3 , filetitle : This is song
1 } ,
{ filename : / songs / file2 . mp3 , filetitle : This is song
2 } ,
{ filename : / songs / file3 . mp3 , filetitle : This is song
3 }
]
);
$. musicplayer . setSongLabel ($. songtitle );
var thisWin =$. index ;
var winTitle = Media Player ;
function doopen ( evt ){
if ( OS_ANDROID ){
thisWin . title = winTitle ;
} else {
$. windowtitle . text = winTitle ;
}
$. musicplayer . updateScreen () ; }
$. index . open () ;
As you can see, our index.js looks much cleaner now that we have removed all media
playing functionality. Now lets see how to implement these features in our widgets
controller.
We start our Widget.js by initializing the variables well be using:
var songs , songLabel = null ;
var currentSong =0;
The code above is very similar to what we used in our previous version. The only
difference is that were initializing songs as null because at this point we dont know
which songs are to be played, and we have a new variable named songLabel, which will
be used to hold a reference to the label to be updated every time the player changes.
You can go ahead and move all the player methods unchanged, that is: prevdown, prevup,
nextdown, nextup, playdown, playup, playsong, stopplayer, moveback, moveforward and
updateScreen. Out of these methods we only need to change two of them: playup and
updateScreen. Playup needs to be updated to use the WPATH function as seen below.
function playup ( evt ){
evt . source . opacity =1;
if (! audioPlayer . isPlaying () ){
evt . source . image = WPATH ( btnstop . png ) ; playsong () ;
} else {
evt . source . image = WPATH ( btnplay . png ) ; stopplayer () ;
}
}
UpdateScreen needs to change so its updating the new variable that points to the outside
label. The change is simple go from this:
function updateScreen () {
$. songtitle . text = songs [ currentSong ]. filetitle ;
}
to this
function updateScreen () {
songLabel . text = songs [ currentSong ]. filetitle ;
}
The only difference is that we changed the object were assigning the filetitle to. In the
original version we were using $.songtitle, which is the Id of the Label inside index.xml.
We no longer have direct access to this object because were working from the widget, so
we created the variable songLabel to become a bridge to this label. Now we only have
three things left to do:
Establish the bridge between songLabel and $.songtitle
Receive the songs array from the host app
Allow the updateScreen method to be called from the outside
For these three well be using the special CommonJS exports object. Since with Alloy
everything is effectively becoming optimized CommonJS modules, we have access to the
exports object, which in turn will allow us to expose methods and properties to our host
app.
To create the bridge between $.songtitle and songLabel, we simply export a function that
will receive the external Label object and will assign it to our internal variable.
exports . setSongLabel = function ( args ) {
songLabel = args ;
When you call this function from index.xml, the songs array will be assigned to the local
songs variable.
Finally, updateScreen exists inside our Widgets controller and we want to expose it as-is,
so we use this syntax.
exports . updateScreen = updateScreen ;
When you call the updateScreen function from index.xml it will be directly calling the
updateScreen function inside the widget.
This completes the implementation of the Widgets functionality. Below youll find the
full listing.
Listing 3.3: Widget.js
var songs , songLabel = null ;
var currentSong =0;
var audioPlayer = Ti . Media . createSound () ;
function prevdown ( evt ){ evt . source . opacity =.5;
}
function nextdown ( evt ) { evt . source . opacity =.5;
}
function prevup ( evt ){ evt . source . opacity =1; moveback () ;
function nextup ( evt ){ evt . source . opacity =1; moveforward () ;
}
function playdown ( evt ) { evt . source . opacity =.5;
}
function playup ( evt ){
evt . source . opacity =1;
if (! audioPlayer . isPlaying () ){
evt . source . image = WPATH ( btnstop . png ) ; playsong () ;
} else {
evt . source . image = WPATH ( btnplay . png ) ; stopplayer () ;
}
}
function playsong ( evt ) {
audioPlayer . url = songs [ currentSong ]. filename ; audioPlayer . play () ;
}
function stopplayer () { audioPlayer . stop () ; audioPlayer . release () ;
}
function moveback () {
if ( currentSong > 0) {
currentSong - -;
if ( audioPlayer . isPlaying () ) {
stopplayer () ;
playsong () ;
}
updateScreen () ;
}
}
if ( audioPlayer . isPlaying () ) { stopplayer () ;
playsong () ;
}
updateScreen () ;
}
}
function moveforward () {
if ( currentSong < songs . length -1) { currentSong ++;
function updateScreen () {
songLabel . text = songs [ currentSong ]. filetitle ;
}
exports . setSongs = function ( args ){ songs = args ;
}
exports . setSongLabel = function ( args ) { songLabel = args ;
}
exports . updateScreen = updateScreen ;
Our widget is complete and we have successfully detached the media player controls,
along with all functionality, to an external component that simplifies our code and adds a
great deal of flexibility. You can now run your app and see that it works just as before.
You probably have noticed that we also introduced a new problem. If you try to switch
themes now, youll see they work for the app but not for the Widget. The Widget has all its
assets stored locally and they are not aware of the change in themes. Lets look at how to
add theming functionality to our Widget.
3.3 Summary
In this chapter youve seen:
How to create Alloy Widgets from scratch
Where to obtain user-created Widgets
How to take functionalities of an existing app and encapsulate as an Alloy Widget.
How to add themes to your Widgets
Widgets are one of Alloys most powerful features, something you dont find in any other
cross-platform mobile development tool. Not everything should be built as a widget, but
as you progress in your development of cross-platform apps youll be able to identify
pieces of your code that could be reusable across apps, making your code cleaner and
easier to maintain.
Part II
Advanced functionality
Chapter 4
directly using Objective-C or Java, but everything from a single well-organized and
maintainable codebase.
When you run this app on a device, theyll be no way of telling that it was built using a
native framework, and thats the ultimate goal. This app will be optimized for handheld
devices, that is iPhone/iPod Touch and Android Phones. Although the app will run on
iPads and Android tablets, no effort will be made to make the screen adjust to large
screens. I will however give you tips on how you can easily make these adjustments.
In this chapter, as well as chapter 5 and 6, Im trying to explain the concepts as we run
into them, as opposed to giving you all the information and letting you figure out when its
needed. As a result, youll see that sometimes Ill ask you to go back to code that was
already written to make changes to it, but this is completely intentional.
Before we start building, lets look at the app requirements and features screen-by-screen.
YouTube also returns a JSON feed, but its much easier to connect to than Twitter. Well
extract from the feed the thumbnail, user, description, video link and duration, and well
display it in a table. By clicking on each row, well open the video on the YouTube app, or
on the browser if the app is not installed.
Now that you know what the end result should look like, lets go ahead and build this app
step-by-step. This app is full of features and interesting cross-platform decisions. I
promise itll be fun. Lets get started!
Open up index.xml, which at this point contains the default window definition. For our
app well use a tab container, called in Titanium TabGroup. The TabGroup is a special
cross-platform object that holds a collection of Tabs. Tab objects themselves are
containers that hold Windows. The basic structure of a TabGroup is as follows.
< Alloy >
< TabGroup >
<Tab >
< Window >
</ Window >
</ Tab >
</ TabGroup >
</ Alloy >
You can have as many tabs as youd like. How they are rendered is different on iOS and
Android. On iOS, if Tabs dont fit on the screen, youll automatically get a More button
that will present a screen with links to all additional tabs. On Android, the tab bar is
scrollable, giving you access to all the tabs from the same screen. Our app has only 4 tabs,
so we wont run into any of these cases.
To create our four tabs, simply add additional tab objects inside the TabGroup tag:
< Alloy >
< TabGroup >
< Tab title = Schedule >
< Window title = Schedule backgroundColor =# FFFFFF > </ Window >
</ Tab >
< Tab title = Venue >
< Window title = Venue backgroundColor =# FFFFFF > </ Window >
</ Tab >
< Tab title = Conversation >
< Window title = Coversation backgroundColor =# FFFFFF > </ Window >
</ Tab >
< Tab title = Videos >
< Window title = Videos backgroundColor =# FFFFFF > </ Window >
</ Tab >
</ TabGroup >
</ Alloy >
If you save and run this project at this point your app will look like Figure 4.7).
not necessarily a good practice. As much as you can, youd want to keep each window as
an external component that is dynamically included into your files. For this Alloy provides
the Require tag. Lets go ahead and create our windows as independent files.
From the Project Explorer, right-click on any file and youll see a pop-up menu. Select
New and then Alloy Controller (Figure 4.8).
name with no extension and Alloy will take care of the rest. You now need to repeat this
process for all your four XML files: schedule, venue, conversation and videos.
Controllers can also be created from the Terminal/Command line. Simply browse to your
project folder and type: alloy generate controller <controller name >, for example alloy
generate controller schedule, and Alloy will create the XML, TSS and JS files with the
given name in the appropriate folder.
You should now have all your controllers created as seen in Figure 4.10.
By default, controllers created by Alloy are Views. If you open, for example
conversation.xml, you can see it for yourself:
If you run the project now youll get the following error.
[ ERROR ] Invalid Tab child Alloy . Require
[ ERROR ] Tab must have only one child element , which must be a Window
[ ERROR ] Alloy compiler failed
The problem is that youre actually inserting a View inside a Tab. The Tab can only hold
Windows, so youll need to change your code to have a Window as its root element.
< Alloy >
< Window class = container >
</ Window >
</ Alloy >
We have are basic tab structure ready to begin styling. Before we do so, notice that were
assigning the title for each tab as a string. If you recall from the beginning of the chapter,
we want to make this app available in different languages. This means that we should not
add any text as a hard-coded string. We need learn about app internationalization.
4.2.2 Internationalization
App internationalization, also known as i18n (I and N the beginning and ending letters of
the word internationalization, and 18 the amount of letter in between), is the practice of
making your app available for international markets. The success of your app is
proportional to the amount of people using it, but its unrealistic to assume everyone
speaks English (or any other language for that matter).
Titanium gives you an easy-to-use, cross-platform mechanism to make your app available
in as many languages as youd like. The language however is not selected on the app
itself, but at the operating system level. If your app is available in Japanese and the users
phone is set to Japanese, your app will be displayed in Japanese.
This is achieved through the use of ISO 639-1 codes. These codes are standard two-letter
codes used to identify a language. Full list can be found at
http://wikipedia.org/wiki/List_of_ISO_639-1_codes. The code for English is en, so go
to your Project Explorer and create a folder structure like the one in Figure 4.11.
XML file with a root resources tag, and entries enclosed in a string tag. Create app title,
tab1 title, tab2 title, tab3 title and tab4 title as seen below. Every time we need to provide a
textual message on the app, we simply come to this file (or files) and create an entry for
that text.
< resources > < string < string < string < string < string
</ resources > name = app_title > The Alloy Conference </ string > name = tab1_title > Schedule </ string >
name = tab2_title > Venue </ string >
name = tab3_title > Conversation </ string > name = tab4_title > Videos </ string >
To access these entries Titanium provides the Ti.Locale namespace, which has a method
called getString, so if youd like to get the value for app title above, you simply call:
var app_title = Ti . Locale . getString ( app_title , The Alloy Conference ) ;
The first argument is the name of the string inside your string file. The second argument,
which is optional, is the default value to return in case the string doesnt exist in the file. In
both of these examples the result is the localized value of the string app title stored in the
app title variable. The function can be used from JavaScript controllers, TSS stylesheets or
XML views. Through the chapter well be using all three.
Titanium saves and caches your localized strings. For this reason there may be moments
when you have added a new string to the language file but it doesnt reflect on your app.
To fix this, go to Project >Clean to force Titanium to rebuild the whole project the next
time you run it. Clean can also be performed from the Terminal/Command Line by typing:
ti clean
Now that we have our Tab names as localized strings, lets start styling our TabGroup. Go
ahead and add IDs to each Tab in index.xml (tab1, tab2, tab3 and tab4) and the
corresponding entry in the Stylesheet. Inside the stylesheet use the L function to fetch the
localized string as shown in Figure 4.12.
Figure 4.12:
Relationship between index.xml, index.tss and language file
Now your code has a good modular base to build on top of and your app will be properly
organized. Now its time to prettify it. Lets start with iOS because as youll see in section
4.2.4, customizing colors in Android is extremely easy.
(Figure 4.13).
Figure 4.16:
Android Image Locations
When Titanium compiles your app, it will grab the images for the platform youre
building for, and will leave the rest behind, making sure that your iOS app doesnt carry
over assets designed for the Android version and vice-versa. In the case of Android, itll
grab all versions of your images and will use the right one depending of the device youre
app is running on.
Now that you have created the images and have stored them in the appropriate folder,
were ready to create our stylesheet (index.tss), shown below.
Listing 4.1: Index.tss
TabGroup [ platform = ios ]:{
tabsTintColor : #6 A9E68 ,
tintColor : # ffffff
}
# tab1 [ platform = ios ]:{
title :L( tab1_title ) , icon : images / schedule . png
}
# tab2 [ platform = ios ]:{
title :L( tab2_title ) , icon : images / venue . png
}
# tab3 [ platform = ios ]:{
title :L( tab3_title ) ,
icon : images / conversation . png
}
# tab4 [ platform = ios ]:{
title :L( tab4_title ) , icon : images / videos . png
}
# tab1 [ platform = android ]:{
icon :/ images / schedule . png
}
# tab2 [ platform = android ]:{ icon :/ images / venue . png
}
# tab3 [ platform = android ]:{
icon :/ images / conversation . png
}
# tab4 [ platform = android ]:{ icon :/ images / videos . png
}
You may need to clean your project after adding images to platform-specific folders
Were almost finished preparing the base structure for our app. One last thing well do
before getting into Android customization is to set the app title on the Android ActionBar,
shown in Figure 4.17.
Figure 4.17:
ActionBar Title
In Chapter 2 and 3 we simply set the window title and it was assigned to the ActionBar.
However, Ill use another method now, just to introduce you the advanced ActionBar
access.
Go ahead and open index.js and attach an event listener to the open event of our TabGroup
($.index). Inside this listener well grab a reference to the Android Activity, then a
reference to the actual ActionBar, and finally set the title directly to it.
$. index . addEventListener ( open , function ( evt ) {
if ( OS_ANDROID ){
var activity = evt . source . getActivity () ; var actionbar = activity . actionBar ;
actionbar . title = The Alloy Conference ;
}
})
$. index . open () ;
Activity is an Android concept, not a Titanium one, and to learn more about it please refer
to http://developer.android.com/reference/android/app/ Activity.html
You can run the app right now and your icons should be showing on both iOS and
Android, the iOS version with a green highlight on the selected Tab.
The icon in the ActionBar is the actual apps icon. You can however specify a different
one assigning it to actionbar.icon in the function above.
Now we need to get our Android version to parity with iOS by applying the appropriate
colors to the Tab Bar, and this is done using Androids native theming system.
The reason for this is because Android has its own, very flexible way of creating themes
and styles for your apps. These themes however are applied to your app at build time
instead of runtime, so you have to bundle the theme with your app and then compile it.
Two such controls are the ActionBar and Android Tabs.
I wont get into details of how Android themes are structured because this is a complex
native Android concept. Ill however show you how you can quickly generate an
ActionBar Style and apply it to your Titanium app.
You can find information about the theme/styles file structure at http:
//developer.android.com/guide/topics/ui/themes.html
To create an ActionBar style for our app, head over to http://jgilfelt. github.io/androidactionbarstylegenerator. This website offers a free, web-based tool to configure the
different colors of your ActionBar Style, as you can see in Figure 4.18.
This online tool is part of a website called Android Assets Studio ( http:
//romannurik.github.io/AndroidAssetStudio/), which I suggest you visit often because it
can help you in generating many different Android-specific graphical assets and settings.
Fill out the fields as shown on Figure 4.18.
Figure 4.18:
Figure 4.19:
Download resources
Click on the Download Zip button, download this file and decompress it. Inside youll
find a res folder. Now go to your apps source code folder, and at the same level of your
tiapp.xml create a folder named platform and inside, one named android. Drop your
decompressed res folder onto this folder, as seen in Figure 4.20.
With these lines youre re-configuring your Android Manifest file (Androids main
configuration file), specifying that the app will use a custom theme named Greenab.
Figure 4.21 shows a comparison of our app with Androids default theme on the left and
our app with the custom GreenAB theme we just created.
Figure 4.21:
Comparison of default and custom Android theme
Before we finish, lets look at one last optional detail. Theres a thin line dividing the
ActionBar from the Tab Bar (Figure 4.22)
Figure 4.22:
ActionBar divider line
4.3. Summary
As mentioned before, these settings are applied to your app at build time, so theres no
bottomborder property. If youd like to remove this line, youll have to modify the
actual image used as background to your ActionBar. This particular image, in the case of
our GreenAb theme is called ab solid greenab.9.png, and can be found in your
platform/android/res folder as seen in Figure 4.23.
Figure 4.23:
ActionBar divider line
You can freely open this file in a bitmap editor and change the color of the bottom line to
match the color of the rest of the image. Just make sure you dont mess with the external
pixels of the image. These pixels are the 9-patch delimiters, used by Android to know the
portions of the image that are stretchable. You can find more information about 9-patch at
http: //developer.android.com/tools/help/draw9patch.html. Also notice that youll have to
change the mdpi, hdpi, xhdpi and xxhdpi versions of the image.
Now you can run your app again and see the changes. Our apps basic structure is now
finalized, so were ready to move to the next chapter and start adding content to the tabs.
4.3 Summary
In this chapter youve seen:
Building a tab-based app
Working with images for different screen sizes and densities
Adding support for multiple languages
Using Androids built-in theme system
We saw that while the TabGroup tag is cross-platform, tabs are rendered different on each
platform. We also saw that on Android we have the ActionBar, always displaying the
name of your app, while on iOS we have the title of the currently selected tab. Learn to
embrace these differences in order to achieve the best user experience possible for each
platform. Dont try to implement and ActionBar-like title bar on iOS, or try to emulate
iOS tabs on Android. Doing this will steer you away from native UI and UX, and in the
end will confuse your user. The end goal is to deliver the same functionalities on each
platform, but rendering the user interface in the way the platform dictates.
We now have a solid foundation for our app. In Chapter 5 well work on the schedule
tab, where well use native Android modules and local databases.
Chapter 5
Figure 5.1: Platform-specific code is not re-usable, but the data and logic is. The decision
of displaying the data differently on iOS and Android is based on usability and overall
user behavior. In this example we want to provide three pages of data (the days) inside one
tab (the schedule). It so happens that on iOS and Android this type of scenario is handled
using different screen controls. Lets start with Android.
Although Titanium allows you to build your UI in any way you want and provide exactly
the same UI and UX across all target platforms, thats a conscious choice. The goal of a
cross-platform developer should always be to build an app with the greatest amount of
reusable code, but still deploy an app that is considered best in class in every platform its
running on. Code reusability is for actual programming logic and not necessarily for user
interface code.
Copying the module to the modules folder is not enough. By adding this line, your app
now knows that it has a module to be loaded for your app. This is the process youll
follow every time you need to add a native module to your app. Id like to emphasize that
we have added a native Android module. This means that this will only work for Android.
Native modules are by definition platform-specific. Keep this in mind, as the features
provided by an Android module will not be available for iOS and vice-versa.
To make sure we only make the ViewPager available for Android, well use Alloys
platform-specific folders. Go ahead and create android and ios folders inside views,
and move schedule.xml to the android folder as seen in Figure 5.4. Do the same thing
with the controllers folder: create ios and android folders and move schedule.js to
the android folder. When your app is compiled, Alloy will grab the views and
controllers that correspond to the platform youre building for.
First thing to notice is that Im grabbing the title of the tabs from the i18n file: make sure
you create entries for day1 caption, day2 caption and day3 caption. Besides this, there are
two things in this listing that require further explaining: the lines referring to
Alloy.Globals that allow you to create globally-accessible properties, and the lines
referring to Alloy.createController that allows you to include controllers inside other
controllers.
Making configuration global
In order to keep all our configuration and settings decoupled from the actual logic, well
use a special Alloy feature that allows us to create global settings in the alloy.js file. Inside
this file you can add settings that will be available to your app just before your actual app
is run. Alloy provides two objects: Alloy.Globals and Alloy.CFG, the former used for
basic global variables and the latter for configuration settings. Well add the following:
In this code were creating a property named ViewPager inside Alloy.Globals. This object
can be modified from outside of our app, and accessible from code by calling
Alloy.Globals.viewPager.property name. From here on out, if we want to change any of
these settings, instead of going to schedule.js, we simply go to alloy.js. I know that
technically speaking the ViewPager colors are actually configuration, but for illustration
purposes Im storing these values as Globals. In Chapter 7 well see how to use
Alloy.CFG.
Inserting controllers dynamically
Controllers can be inserted into other controllers. As I mentioned earlier, when you create
a controller that is not the base of your window, you dont need to have a TabGroup of a
Window as your base tag; your controller could be any piece of code, like a View, a
Button, etc., that youd like to keep as a separate file in order to ensure reuse throughout
your code.
Go ahead and create three new controllers called day1, day2 and day3, and dont do
anything to them. These will be used to hold the contents of each days schedule and were
creating them as external files to ensure reusability across platforms.
In Listing 5.1 you can see how were inserting these views using the following syntax
shown in Figure 5.5.
You can chain both calls (in fact this is the recommended way) in a single line like:
var view = Alloy . createController ( controller_name ,
controller_arguments ). getView () ;
In this example our controllers are simple. If your controllers have complex logic that
could generate errors, I suggest you wrap your Alloy.createController call in a Try/Catch
block
Your schedule.js for Android is ready, using the external ViewPager module, grabbing
configuration settings from alloy.js and inserting external views. Since the module is
instantiated from within the controller, theres no need for special XML tags. If you recall
from listing 5.1, once the tabs are created theyre inserted into the view with the line
$. schedule . add ( tabs );
The schedule.xml file is simple as it only needs to provide you with the $.schedule
container as follows.
< Alloy >
< Window id = schedule class = container layout = vertical > </ Window >
</ Alloy >
Finally, the schedule.tss up to this point simply holds the styles the $.schedule container,
as seen below.
. container [ platform = android ] : {
backgroundColor : # fff ,
barColor : #6 A9E68 ,
title : L ( tab1_title ) ,
orientationModes :[ Ti . UI . PORTRAIT , Ti . UI . LANDSCAPE_LEFT , Ti .
UI . LANDSCAPE_RIGHT ],
layout : vertical
}
Here we used another localized string so dont forget to add tab1 title to your i18n files.
Youre now ready to test the app and see how were doing. It should look like Figure 5.6.
Figure 5.7: Recap of our goal: Two platform-specific containers with common content
For our iOS container well use two different components. The first one is the TabbedBar,
which creates the strip of buttons on the top, shown in Figure 5.8. In the iOS SDK, this
control is called Segmented Control.
Figure 5.8:
TabbedBar and ScrollableView on iOS
For the actual containers well use a ScrollableView, a control to which you add as many
views as needed and allows you to scroll through them. Well then use event listeners to
synchronize the movement of the views with the actual buttons and vice versa.
To begin, just like before, create a controller named schedule, either through Studio or the
CLI. Make sure that just like before you move schedule.xml to /views/ios/schedule.xml
and schedule.js to /views/ios/schedule.js. Now, open the schedule.xml for iOS and lets
define our view, shown in listing 5.2.
Listing 5.2: /ios/schedule.xml
< Alloy >
< Window class = container >
< TabbedBar id = dayoptions platform = ios onClick = changeday >
< Labels >
< Label class = day1button / > < Label class = day1button / > < Label class = day2button / >
< Label class = day3button / > </ Labels >
</ TabbedBar >
< ScrollableView pageChange > < Require id = scrollableView onScroll =
src = day1 / > < Require src = day2 / >
< Require src = day3 / >
</ ScrollableView >
</ Window >
</ Alloy >
In the listing above we defined two event listeners. First we have an onClick for the
TabbedBar. This event will receive the ordinal of the button that was pressed and well use
this value to set the currentPage property of the ScrollableView. The second event is an
onScroll, which will give us the ordinal of the current page, which will use to set the
selected button. These two events effectively synchronize the TabbedBar and the
ScrollableView to work together as one. Listing 5.3 shows the implementation of these
two events in schedule.js.
I added the onScroll event just to show how to listen to scroll events and synchronize them
with the TabbedBar. Adding horizontal scrolling to the Segmented Control is entirely
optional
Finally we need to complete the stylesheet, which we started with the Android version.
Simply merge the values in listing 5.4 with the ones we already added for the Android
version.
Listing 5.4: /styles/schedule.tss
. container [ platform = ios ] : { backgroundColor : # fff , barColor : #6 A9E68 , title : L ( tab1_title ) ,
orientationModes :[ Ti . UI . PORTRAIT , Ti . UI . LANDSCAPE_LEFT , Ti .
UI . LANDSCAPE_RIGHT , Ti . UI . UPSIDE_PORTRAIT ], layout : vertical ,
statusBarStyle : Titanium . UI . iPhone . StatusBar . LIGHT_CONTENT
}
# scrollableView :{
showPagingControl : false , scrollingEnabled : true
}
# dayoptions [ platform = ios ]:{ backgroundColor :#6 A9E68 , top :5 ,
bottom :5 ,
height :25 ,
width :200 ,
index :0
}
. day1button :{
title :L( day1_caption )
}
. day2button :{
title :L( day2_caption )
}
. day3button :{
title :L( day3_caption )
}
After performing these changes, go ahead and compile the app for iOS (shown in Figure
5.9) and youll see that we have it up to parity with the Android version.
technical and for these cases you should use the ListView, which well explore in the
following chapter.
Creating a TableView is really straightforward. For example, to create a simple list with
the numbers from 1 to 5, youd write something like this.
< TableView >
< TableViewRow >
< Label >1 </ Label >
</ TableViewRow >
< TableViewRow >
< Label >2 </ Label >
</ TableViewRow >
< TableViewRow >
< Label >3 </ Label >
</ TableViewRow >
< TableViewRow >
< Label >4 </ Label >
</ TableViewRow >
< TableViewRow >
< Label >5 </ Label >
</ TableViewRow >
</ TableView >
Figure 5.11: TableView and TableViewRow All rows are identical, the only change is their
content. Each row will be an actual container in which well create two columns. The left
column well use for the time and location, and the right column well use of the
description of the talk, as seen in Figure 5.12.
In Chapter 2, I mentioned that Alloy has a special file called app.tss, to which you can add
global styles that will be available to all your views. Since all three pages - Day 1, Day 2
and Day 3 - have exactly the same visual aspect, we can work with Day 1, and then
replicate the procedure for the rest of the pages. By adding our styles for day1, day2 and
day3 to app.tss, well be able to
define them only once, and not replicate them in their individual stylesheets. Go ahead and
create an empty file named app.tss inside app/styles. Make sure you check the companion
code for this section to see the full stylesheet. Below Ill explain the most important
aspects of it.
. table [ platform = ios ]:{
separatorStyle : Titanium . UI . iPhone . TableViewSeparatorStyle . NONE
}
. row [ platform = ios ]:{
height :155 ,
selectionStyle : Titanium . UI . iPhone . TableViewCellSelectionStyle .
NONE
}
. row [ platform = android ]:{ height :155
}
. timecol :{
width :40% ,
left :0 ,
top :5 ,
bottom :5 ,
layout : vertical
}
. descriptioncol :{
width :60% ,
right :5 ,
backgroundColor :# efefef , top :5 ,
bottom :5 ,
layout : vertical
}
The separatorStyle property of the TableView allows you to specify whether you want a
line separating the rows or not. Were setting this property to show no separator. Were
then disabling the row selection on iOS, and this for aesthetics only. Finally were setting
the left property of the Time Column to 0 and the right property of the Description
Column to 5 to make sure the content aligns correctly on the screen.
Figure 5.13 shows how the app looks so far.
Figure 5.13:
TableView on iOS and Android
In this section we saw how to create a cross-platform TableView and how to use Alloy
global styles to share styling across similar views. The following section covers data
access. Well be looking at three different techniques, so consider this point a checkpoint
well be coming back to.
Figure 5.15:
Location of data files
Now that we have the data files ready, we can go ahead and build the routines that will
load the data onto the tables. At this point we want to step back and remember that all
three pages, Day 1, Day 2 and Day 3 are exactly the same, and the only thing that changes
is the data they show. You may feel tempted to write the code for Day 1 and then copy and
paste it to Day 2 and Day 3, and although this works, its not the best approach; the right
approach is building a CommonJS module you can reuse for all three pages. This will
ensure that your code is modular, and since all three pages work in the same way, making
a change in the module will automatically take effect if all three.
The function above takes care of loading the data from the data files. Also youll notice
that its receiving a variable named day. This is because everything we write in this
module must be generic, and by sending the day to work with, we make sure this function
will work for all three days.
To call this function, simply open day1.js, create the day variable and call the module,
as seen below.
var dayTag = day1 ;
require ( schedulemod ) . loadSchedule ( dayTag );
At this point your data is properly loaded into memory. Now lets see how to create our
row template and load them into the TableView. Create a new controller named dayrow,
either using the Studio menus or the CLI. This will create dayrow.js, dayrow.xml and
dayrow.tss. This last file we wont use because our styles are global in app.tss, but you can
leave the file empty if you wish. Open day.xml where we created our TableView Row, and
move the whole row to dayrow.xml, as illustrated in Figure 5.16.
You may have noticed in this function were using the second argument of
Alloy.createController. When you call Alloy.createController(dayrow), Alloy loads the
dayrow.xml file as an Object and when you call getView() its converted into a Titanium
View. Now, by adding the second argument, youre actually sending data to dayrow.
Figure 5.17 illustrates how data is being inserted into your code.
Figure 5.17:
Creating controllers dynamically
Youre looping through all of the items in Ti.App.Properties.getObject(daytag). For each
item in this Array youre first adding the actual index so we can keep track of the order in
which they came. Then we call createController and we send the whole object. Alloy will
look for dayrow.xml, but before giving it to you, itll call dayrow.js and will pass along
the object in the item variable. The code in dayrow.js, and all controllers for that matter
start with this line:
var args = arguments [0] || {};
After this line is executed, Alloy assigns the contents of the item variable to the args
variable, so from your controller code youve just gained access to the data passed by
createController. This all happens before your dayrow.xml is rendered, giving you the
opportunity to add values to your view. In Figure 5.18 you can see how data is being
passed from dayrow.js to dayrow.xml.
Figure 5.18:
Populating data in views
So in summary, for every iteration of the loop, the current item in the JSON object is being
sent to dayrow.xml, this data is being used to populate the XML file, rendered and
returned to the variable row. Each row is being collected in the Array named tableData.
Once weve gone through all the rows, we have an Array of TableViewRows that we then
set on the TableView by calling setData.
Next lets make sure we call the loadTable function by going to day1.js and make adding a
call like so:
require ( schedulemod ) . loadTable ($. day1table , dayTag );
Notice that since this is a generic function, we need to send the table to which the data will
be added and the dayTag variable used to grab the data from the persistent property.
Day 1 is ready. Now to enable the list for Day 2 and Day 3, simply copy the contents of
day1.xml over to day2.xml and day3.xml, making sure you change the id of the table.
< Alloy >
< TableView id = day1table class = table > </ TableView >
</ Alloy >
Then copy the contents of day1.js over to day2.js and day3.js changing the dayTag
variable and the reference to the table to be used.
var args = arguments [0] || {};
var dayTag = day1 ;
require ( schedulemod ) . loadSchedule ( dayTag );
require ( schedulemod ) . loadTable ($. day1table , dayTag );
This completes the loading of schedule data using JSON files and persistent properties.
Now lets explore how to achieve the same results but using local SQLite databases.
5.4 SQLite
Both iOS and Android have native support for SQLite databases. SQLite is a server-less
database library that allows you to have a database that you access locally. The main
advantage is that the data will come pre-loaded with the app, instead of having to load it at
runtime as well as store or modify data at runtime. Going forward, SQLite will give you
the flexibility of having multiple tables and query them using SQL Syntax, although thatll
not be needed for our app.
To create databases you need to use third-party software. In my opinion, the easiest is the
free SQLite Manager Firefox Extension you can find at https://addons.mozilla.org/enUS/firefox/addon/sqlite-manager/. Download and Install this extension on your Firefox
browser, and then go to Firefox >Tools >SQLite manager to launch it. Select Database
>New Database and enter scheduledata for its name and then select your app/assets
folder as its home (Figure 5.19). This will create the file scheduledata.sqlite in /app/assets.
Figure 5.19: Create SQLite Database Youve successfully created an empty database. Now
we need to import our JSON files onto it. SQLite Manager has an import function, but
only accepts SQL and CSV files. The easiest way to go around this is to convert our JSON
files to CSV, and for this we can use a free online tool called ConvertCSV found at
http://www.convertcsv.com/json-to-csv.htm, shown in Figure 5.20.
Figure 5.20:
Convert JSON to CSV
On this page, select your day1.json and click on Convert JSON to CSV. This will load
your JSON file and display a comma-separated values version below. The Include header
in first row checkbox will make the next step easier. Then select Save to Disk to save the
CSV file in your local computer.
Now go back to your SQLite Manager and select Import Wizard. From this screen select
the file from the previous step, enter day1 as the database name and if the first row
contains the column names, make sure you specify it in this screen as seen in Figure 5.21
Figure 5.22:
Confirm Table Schema
Figure 5.23 shows the results after importing the CSV file. You can make changes to the
data at this point, or add new records if needed.
Then open up schedulemod.js and delete the loadData function and its export. Then, we
need to modify loadTable so its now using the JSSQL library instead of global properties.
function loadTable ( tableObject , dayTag ){
var DBH = require ( com . alcoapps . dbhelper ) ;
var db = new DBH . dbhelper ( / scheduledata . sqlite , scheduledata ) ; var scheduleData = db . get ({
fields : *,
table : dayTag
}) ;
var tableData =[];
scheduleData . forEach ( function ( item , index ){
item . index = index ; // add the index to the object
var row = Alloy . createController ( dayrow , item ). getView () ;
tableData . push ( row ) ;
})
tableObject . setData ( tableData );
By calling db.get, were actually querying the SQLite database for all the records in the
table named as the dayTag variable, and the results are a JSON Object. This means that
from here on out, the code remains the same as before.
iOS local database location
Its important to keep in mind that when you ship a database with your app, the phone will
actually move it to a different location, this because on iOS the apps directory is readonly. This however creates a small problem. Once your apps database is used for the first
time, its there for the lifetime of your app, so if you ship a different version of the
database with a new build, the database will still be untouched because it already exists.
You can know the location of the database by calling the file property on the database
object (http://docs.appcelerator.com/titanium/3.0/#! /api/Titanium.Database.DB-propertyfile).
If you need to make changes to an existing database as part of a new version of your app,
youll need to use the Database functions to perform the required changes. The JSSQL
library contains methods for creating new tables, editing, deleting, etc., or you could also
use regular SQL Strings against the database.
Its also important to remember that SQLite databases are not encrypted; theyre not text
files, but dont offer any built-in security. If you plan on storing sensitive data, Id suggest
you encrypt it before saving. There are many JavaScript-based encryption libraries, or you
could use the Securely Titanium module found at
https://github.com/benbahrenburg/Securely, which besides string encryption offers a
whole security layer for your Titanium apps.
myindex : INTEGER
},
adapter : {
type : sql ,
collection_name : day1
}
},
extendModel : function ( Model ) {
_. extend ( Model . prototype , {
// extended functions and properties go here
}) ;
return Model ;
},
extendCollection : function ( Collection ) {
_. extend ( Collection . prototype , {
// extended functions and properties go here
}) ;
return Collection ; }
};
You can look at Alloy models as a combination for our two previous methods: loading
data from JSON files and storing data in SQLite databases. The fact is that Alloy Models
provide an abstraction so you can load and manipulate data in SQLite databases without
having to learn the Titanium.Database syntax. If youre a web developer and already know
BackBone, then youll feel right at home and will be able to leverage its features and
functionality in your Titanium apps.
From the JavaScript code standpoint, its not that much different either. Since our code is
modular and organized, the only changes we need to make in our code are on
schedulemod.js. First lets take a look at our modified loadSchedule function.
function loadSchedule ( day ) {
if (! Ti . App . Properties . getBool ( day +- loaded , false )){ var filename = / data / + day + . json ;
var f = Ti . Filesystem . getFile ( Ti . Filesystem . resourcesDirectory
, filename ) ;
var contents = JSON . parse (f. read () );
contents . forEach ( function ( thisDay , index ) {
var model = Alloy . createModel ( day , {
time
room
title
speaker
description
speakerbio
myindex
}) ;
model . save () ;
}) ;
contents = null ;
f = null ;
: thisDay . time ,
: thisDay . room ,
: thisDay . title ,
: thisDay . speaker ,
: thisDay . description ,
: thisDay . speakerbio ,
: index
Ti . App . Properties . setBool ( day +- loaded , true ) ; }
}
Just like before, we need to load the JSON file using the FileSystem function. Once we
have it, we use JSON.parse to convert it to an object. We didnt do this before because this
conversion was performed by the Ti.App.Properties.setObject method. Once we have our
data in JSON format, we loop through them, and use the Alloy.createModel method to
create a variable with the structure of this data model, and assign the values to its
properties. We then save the model and continue with the next record. After this function
finishes, we have effectively loaded all the records into an SQLite database, all handled
invisibly to you by Alloy and Backbone. Now lets take a look at our modified loadTable
function.
function loadTable ( tableObject , dayTag ){
var tableData =[];
var day = Alloy . Collections . instance ( dayTag );
day . fetch () ;
var dayJSON = day . toJSON () ;
dayJSON . forEach ( function ( item , index ){
item . index = index ;
var row = Alloy . createController ( dayrow , item ). getView () ; tableData . push ( row ) ;
})
tableObject . setData ( tableData );
}
Theres much more to Alloy Models, but after all, its not so much an Alloy feature, but
the features of BackBone.js. If youd like to learn more visit BackBones website at
http://backbonejs.org/. If youd like to learn about other ways of using Backbone with
Alloy you can go to Alloys official Github repository at
https://github.com/appcelerator/alloy/tree/master/test/apps
Knowing that our data exists, we can load an instance of a collection for the day were
working with. We then call the fetch() method to load all the data into the collection and
use the toJSON() method to convert it back to JSON. After doing this, all the rest is the
same as before.
Figure 5.26:
TalkDetails Screen
To enable click events in our TableView, we simply add an onClick event listener to our
TableView tag.
< TableView id = day1table class = table onClick = tableclick / >
Im using Day 1 for this example, but once were done, well need to replicate the
procedure for Day 2 and Day 3. The tableclick function used as the callback for the
onClick event needs to be defined inside day1.js.
function tableclick ( evt ){
}
From this click event well be grabbing data from the clicked row, and sending it over to
our new window. However, the row doesnt contain all the data we need; were missing
the speakerbio, so before we do anything, we need to call our database and get the full
details of the row that was clicked.
In our next two versions well be using SQL Statements. If you dont know how to create
SQL Strings, please refer to http://www.sqlite.org/lang.html for a reference on the SQL
Syntax compatible with SQLite.
SQLite Database version
In the case of our SQLite Database, we didnt explicitly store the index. However, SQLite
automatically sets a hidden property named rowid, which we can use in the same manner
as our previous scenario.
function getTalkDetails ( day , row ){
var DBH = require ( com . alcoapps . dbhelper ) ;
var db = new DBH . dbhelper ( / scheduledata . sqlite ,
scheduledata ) ;
var rec = db . exec ( SELECT * from + day + where rowid = + parseInt ( row +1) );
return rec [0];
}
exports . getTalkDetails = getTalkDetails ;
In our SQLite scenario, the internal rowid starts at 1 instead of 0, so we need to add one to
the row id we receive from our TableView. Also, notice Im converting the value to
Integer, otherwise JavaScript will treat the addition operation as a string concatenation.
Alloy Models version
With Alloy Models the procedure is similar to the SQLite version. As you remember, we
added the myindex field to our data model, using it for the same purpose as with the
version using Persistent Properties. In this version of the function well be using an SQL
String, querying for the myindex field, and sending the SQL String straight to the
Backbone collection.
function getTalkDetails ( day , row ){
var schedule = Alloy . Collections . instance ( day );
schedule . fetch ({ query : SELECT * FROM + day + WHERE myindex =
+ row }) ;
return schedule . toJSON () [0];
}
exports . getTalkDetails = getTalkDetails ;
Now lets go back to our tableclick event callback, from where well be calling the
getTalkDetails function. The function requires the day and row arguments. The day is easy
because its the dayTag variable we already have. The clicked row however we need to
grab it from the actual TableView. The evt variable contains the object described in Figure
5.27.
You can see our generic method inside schedulemod in listing 5.6. Listing 5.5: Generic
tableClick function
function tableClick ( evt , talkDetails , dayTag ){ var w= Alloy . createController ( talkdetails ,{ rowId : evt . row . rowId ,
talkDetails : talkDetails ,
day : dayTag
}) . getView () ;
w. addEventListener ( open , function ( evt ){
if ( OS_ANDROID ){
var activity = evt . source . getActivity () ;
var actionbar = activity . actionBar ;
actionbar . title = talkDetails . title ;
if ( talkDetails . speaker !== ) {
actionbar . subtitle = String . format ( L( presented_by ) , talkDetails . speaker );
}
actionbar . displayHomeAsUp = true ;
actionbar . onHomeIconItemSelected = function () {
The function above will work for all three days. By keeping it inside shedulemod.js you
make sure your code is kept modular and organized and if you need to make any changes,
you make them in a single place.
Notice that we opened the window using the line
Alloy . Globals . tabGroup . activeTab . open (w ,{ animated : true }) ;
This global variable doesnt exist at the moment, but we need it because we cant access
the TabGroup from here. Were running from an external CommonJS module that runs
independent from the apps context. To make it available we add the following line to
index.js.
Alloy . Globals . tabGroup = $. index ;
Whats so important about this? Well this opens the new window inside the current Tab.
On Android this has no real effect because on Android tabs are shallow: theres no
navigation inside them. On iOS however, you can have navigation inside each Tab, so by
opening your window using this line, youre effectively creating a Navigation Controller
inside your tab, as seen in Figure 5.28.
In the code above were adding all of our screen components to a ScrollView. Since we
dont have control over the size of the data on this screen, there may be cases where the
speaker bio will not fit on the screen. By adding all of our UI components to a ScrollView,
we make sure that everything fits perfectly and those components that fall outside of the
visible area are still available by scrolling up and down. Next, we need to apply the styling
for this screen. Theres nothing new in this stylesheet, so make sure you check the
companion code for a full listing.
Finally, our talkdetails.js (listing 5.7) receives the data from our tableClick function and
assembles it in the XML, just like we did before with our row template.
Listing 5.7: talkdetails.js
var args = arguments [0] || {}; var talkDetails = args . talkDetails ;
$. talktitle . text $. time . text = $. room . text = $. description . text = talkDetails . title ;
talkDetails . time ;
talkDetails . room ;
= talkDetails . description ;
if ( talkDetails . speaker !== && talkDetails . speaker !== null ){ $. speaker . text = String . format (L( about ) , talkDetails . speaker );
} else {
$. speaker . text = ;
}
if ( talkDetails . speakerbio !== && talkDetails . speakerbio !== null ){
$. speakerbio . text = talkDetails . speakerbio ;
} else {
$. speakerbio . text = ;
}
function closewindow ( evt ){ $. talkdetails . close () ;
}
One last thing before we finish, if you run the app at this point, youll notice a minor
problem (Figure 5.29).
Then open day1.xml, day2.xml and day3.xml and add this event listener to the TableView
tag.
< TableView id = day1table class = table onClick = tableclick onScroll = tablescroll >
This completes our implementation of the Schedule Tab. You can run your app at this
point and see how it effectively shows the schedule data in both iOS and Android using
the platform-specific UI elements we selected. 5.6. Summary
5.6 Summary
In this chapter weve dealt with several important topics that will apply to all your apps.
Third-party native modules
There will be times when a certain feature or screen control is not available in the
Titanium API. For these cases you should explore the large array of third-party native
modules that are available. Most of these can be found in the official marketplace at
http://marketplace.appcelerator.com.
Working with Table Views
We learned how to implement Table Views to display data in a list that scrolls vertically,
just like the one in the Twitter and Facebook mobile apps. In the process of implementing
it, we saw how to use Alloy to load dynamic data onto the table and how to respond to
table clicks to open secondary windows.
Implementing databases via App Properties, SQLite databases and Alloy Models
The data strategy to use is always to be decided in a case-by-case basis. There will be
times when Properties are enough, while there may be other times when you want to use
Backbone models. Make sure you analyze your future projects so you can decide on the
best strategy for you.
Through this chapter we explored three methods of data access, which resulted in three
different projects, each with its own set of programming routines to access the data
sources. The three projects are included with this chapter and I suggest you run them and
use them to follow along with your reading.
In chapter 6 well implement the remaining functionalities of our app, using web services,
Twitter integration and well learn how to open web pages and open the phones dialer.
Chapter 6
To make it easy to maintain, the title and description will be stored in the language files
(you already know how to set these.) The venueAddress and venuePhoneNumber will be
stored as Alloy Global Configuration in config.json.
global : {
venueAddress : 747 Howard St , San Francisco , CA 94103 ,
venuePhoneNumber : + (415) 974 -4000
}
We then create our controller, as shown in listing 6.1. Listing 6.1: venue.js
In the listing above Im using the iOS-only function canOpenURL. The reason is
because iOS apps can be used on an iPod Touch or iPads, and these devices have no phone
functionality. By using this function we make sure that we only try to initiate a phone call
if it is at all possible.
This takes care of the Venue Tab. Make sure you checkout the stylesheet in the companion
code. Now lets move on to the Conversation tab, where well learn to use the ListView
component.
This list style worked perfectly fine for our schedule because its only displaying text data.
The problem with this implementation is that Titanium, as a bridge between native code
and JavaScript, needs to keep an active link between the native component and the
JavaScript one, and the more complex the rows get, the heavier the memory load for your
app. To fix this, Appcelerator developed the ListView component, which you essentially
use for the same purpose, but differs in its technical implementation and its usage. Figure
6.2 shows our desired output.
Figure 6.2:
Conversation Window showing latest tweets
As you can see, the end result is just like the TableView, a vertical list. Lets now look at
how to write the markup for this list.
As you can see in Figure 6.3, you first declare the ListView. Just like with the TableView
the event listener is applied at this level and not at the row level. Then you have a
Template to which you add the templates you wish to use for the different rows of this list,
and this has a very important implication.
Inside a ListView you can have rows that follow different content structures. For example
you can have one row that has an image on the left, the following row with an image on
the right and the remaining rows with no images. For each of these rows youd create
different templates. In this example were creating the loading template, which defines a
row that will be shown to indicate that the list is refreshing. Next we define a template
with name rowtemplate, which declares the actual structure of each data row. Finally,
before we close the ListView tag, we need to add the ListSections, which is basically the
container that will hold the actual list data. Listing 6.2 shows how conversation.xml looks.
Listing 6.2: conversation.xml
< Alloy >
< Window id = conversation class = container >
< RightNavButton platform = ios >
< Button systemButton = Ti . UI . iPhone . SystemButton . REFRESH
onClick = dorefresh / >
</ RightNavButton >
< ListView id = tweetlist onItemclick = doclick >
< Templates >
< ItemTemplate name = rowtemplate
< ImageView bindId = avatar
class = rowtemplate > class = avatar / >
< Label bindId = name class = name / > < Label bindId = screen_name class = screen_name
/ >
< Label bindId = tweet class = tweet / > </ ItemTemplate >
< ItemTemplate name = loadingtemplate class =
loadingtemplate >
< Label bindId = loading class = loading / >
</ ItemTemplate >
</ Templates >
< ListSection name = notification / >
< ListSection name = tweets / >
</ ListView >
</ Window >
</ Alloy >
Notice in the listing above that were adding an iOS-only refresh button. On iOS buttons
go in the Title bar, which in this case is added to each Tab using the special
<LeftNavButton >and <RightNavButton >tags. On Android however, the refresh button
goes in the ActionBar, which we cant access from here. Well deal with it later from
index.js
The Stylesheet for this window has nothing special so Ill leave it to you to check it out in
the source code for the chapter. So far we have the screen structure to display the data. The
data for this screen will come from Twitter, so lets learn how to connect to Twitter.
scope of this book. Well however use a Twitter JavaScript library named codebird.js,
which was modified for Titanium by one member of the Titanium user community and
can be found at https://gist.github.com/viezel/5781083.
Download the library and place it in your app/lib folder. With this we take care of all the
complexity of connecting to Twitter. Its important to know that this library gives you
access to perform a host of different operations against twitter, such as complex searches
and posting tweets. However for our app well only be reading the feed based on a given
hashtag.
To use the library, we need to formally register a new Twitter consumer app. To register,
head over to https://apps.twitter.com/ to obtain your Twitter API Keys. Select Create
New App from the main screen, as seen in Figure 6.4.
Now that we have our keys and our Codebird.js library, well write the code to speak to
the library. Well create another module in /lib which well call socialfeeds.js. This will be
the module that will actually talk to the Codebird library, and since well also need to
implement YouTube feeds, well use it to store all our social code. Listing 6.3 shows the
code for socialfeeds.js. Notice that this is not really Titanium or Appcelerator code, so Ive
abstracted it as much as possible. The usage is the same as seen in the code repo at https:
//gist.github.com/viezel/5781083, but using our keys and structured in a much more
modular way.
Listing 6.3: socialfeeds.js
var Codebird = require ( codebird ) ; var cb = new Codebird () ;
var getFeed = function ( args ){
switch ( args . type ){
case TWITTER :
cb . setConsumerKey ( args . consumerKey , args . consumerSecret ) ; var bearerToken = Ti . App . Properties . getString ( TwitterBearerToken , null
);
if ( bearerToken == null ){
cb . __call (
oauth2_token ,
{} ,
function ( reply ) {
var bearer_token = reply . access_token ; cb . setBearerToken ( bearer_token );
Ti . App . Properties . setString ( TwitterBearerToken ,
bearer_token );
fetchTwitter ( args . action , args . searchstring , args .
max , args . success );
}
);
} else {
cb . setBearerToken ( bearerToken );
This module is designed to be completely generic and its receiving all the data its needs
to operate. The args function receives an object with the following properties:
Type
Action
SearchString
ConsumerKey ConsumerSecret Success
The string TWITTER
The string search tweets
Hashtag to search for, in our case Alloy.Globals.twitterHashTag
Alloy.Globals.twitterConsumerKey
Alloy.Globals.twitterConsumerSecret
Callback to execute on success
var row = {
id : id ,
status : status , created_at : created_at , name : name . trim () ,
screen_name : screen_name . trim () , user_avatar : user_avatar , url : url
}
data . push ( row );
})
var listItems = _. map ( data , function ( item ) { return {
avatar : { image : item . user_avatar }, name : { text : item . name } ,
screen_name : { text : @ + item . screen_name } , tweet : { text : item . status },
properties : {
url : item . url ,
id : item .id ,
screen_name item . screenname
}
};
}) ;
In the code above you can see that were getting the data returned from Twitter and were
only grabbing a couple fields from it. To know which fields are available to you, simply
send the response to the console before parsing like so:
console . log ( response ) ;
var parsed = JSON . parse ( response );
jsoneditoronline.org.
Now, even though we have our data neatly formatted, we have it in a format thats using
the field names returned by Twitter. Whats more, even if you change the property names,
the ListView wont accept the data in this format.
ListView data needs to be formatted in a special way that matches the items we added to
the View. To convert this object into an object that is ListView-friendly, well use the map
function from underscore.js, which will accepts an array in one format, and will return it
in another.
var listItems = _. map ( data , function ( item ) {
return {
avatar
name
screen_name
},
tweet
properties : {
url
id
: { image : item . user_avatar } , : { text : item . name },
: { text : @ + item . screen_name
: { text : item . status } ,
: item . url ,
: item .id ,
screen_name : item . screenname }
};
}) ;
Underscore.js is a JavaScript library full of utility functions. Underscore comes built into
Alloy, so anytime you need to use an Underscore function, you simply call it by
prepending . to is, for example var x = .map(data,function). For more information about
Underscore.js, visit the official website at http: //underscorejs.org/
After calling the map function with our data array, weve successfully transformed our
data into the following format.
{
avatar : {
image : http :// pbs . twimg . com / profile_images /37851/7 f_normal . png
},
name : {
text : TiDev
},
screen_name : {
text : @tidevio
},
tweet : {
text : @thewarpedcoder @jhaynie do you have your # ticonnect
},
properties : {
url : https :// twitter . com / tidevio / status
/526261624064462849 ,
id : 526261624064462849
}
}
With the data in this format, we send it straight to the ListView. Figure 6.9 shows the
relationship of this data format with our ListView markup, explaining why we needed to
transform it.
Weve successfully connected to Twitter, extracted data and have added it to our ListView.
We still have one little detail to take care of. As you can see in Figure 6.10, our iOS
version has a working refresh button in the TitleBar because we added it as part of the
Windows markup. Our Android version is missing this button.
Figure 6.10:
Missing refresh button on Android
ActionBar menu items are usually easy to add by simply using the <Menu >tag. The
challenge we have right now is that as opposed to iOS where the Titlebar belongs to the
Window, on Android the ActionBar belongs to the TabGroup. This means that the
TabGroup is the one that can add and remove buttons to the ActionBar. Additionally, in
our scenario, tab 0 and 1 have no buttons, while 2 and 3 need to have a Refresh button,
which are different to one another. We need to be able to change buttons and their
operations as the user changes tabs. We created the TabGroup in index.js, so lets go there
and make the necessary changes to account for this.
For more information on the <Menu >tag, used to add ActionBar menu items to non-tab
windows, refer to the official documentation at http://docs.
appcelerator.com/titanium/3.0/#!/api/Titanium.Android.Menu
When the Window is inside the tab, the procedure is different. In order to change
ActionBar icons for different Tabs, there are three things we need to know:
How to tell in which tab were on
How to remove any icons that are currently on the ActionBar
How to add icons that are relevant to the tab were currently on
Well start by creating a global variable that will keep the value of the current tab.
Remember, were doing all this inside index.js.
Alloy . Globals . currentTab =0;
Next well add an event listener to the focus event of the TabGroup, and from that event
well grab the index of the current tab, and in the process well use the Android
invalidateOptionsMenu function to tell Android that we want to change the menu items on
the ActionBar.
Alloy . Globals . tabGroup . addEventListener ( focus , function ( evt ) { if ( typeof evt . index !== undefined ) {
activity . invalidateOptionsMenu () ;
Alloy . Globals . currentTab = evt . index ;
}
}) ;
Every time you call invalidateOptionsMenu, Android will call the onCreateOptionsMenu
function, which will allow us to add options to the ActionBar. This function is empty by
default, so we simply need to add a callback to it.
invalidateOptionsMenu and onCreateOptionsMenu are not Titanium functions, but
Android functions. Remember that as a native framework, Appcelerator exposes to you
the functions of the native SDKs, and these two functions are part of the Android Activity.
You can read more about this in the Android official documentation at
http://developer.android.com/ guide/topics/ui/menus.html#options-menu
activity . onCreateOptionsMenu = function (e) { var item , menu ;
menu = e. menu ;
menu . clear () ;
switch ( Alloy . Globals . currentTab ){
case 2:
item = e. menu . add ({
title : L( refresh ) ,
showAsAction : Ti . Android . SHOW_AS_ACTION_ALWAYS , icon : / images / refresh . png
}) ;
item . addEventListener ( click , function (e) { if ( Ti . Network . online ){
if ($ . conversationTab . tweetlist . sections [0]. items . length ===0) {
$. conversationTab . tweetlist . sections [0]. insertItemsAt (0 ,[{
template : loadingtemplate ,
loading :{ text :L( refreshing ) }
}])
require ( conversationmod ) . refreshList ($ . conversationTab . tweetlist );
}
} else {
alert (L( offline_error ) );
}
}) ;
break ;
}
After adding this callback, you can run your app on Android and youll have a refresh
button on the ActionBar, showing only on the Conversation tab, as seen in Figure 6.11.
From our doclick callback, which lives inside conversation.js, we need to obtain the row
that was clicked, because inside this row we have added information about this tweet.
function doclick ( evt ){
var section var item = = $ . tweetlist . sections [ evt . sectionIndex ];
section . getItemAt ( evt . itemIndex ) ;
openApp ({ appUrl : twitter :// status ? id = + item . properties .id , webUrl : item . properties . url
}) ;
}
We now need to create the openApp function, which receives and object with two
properties: appURL, which is a URL using App URL Schemes, and the Web URL to this
Tweet. App URL Schemes are URLs that get registered on the device, which will allow
you to open other apps. In this case what were trying to do is open the actual Twitter app,
if it is installed, otherwise open the devices web browser. The OpenApp function looks as
follows.
function openApp ( obj ) {
if ( OS_ANDROID ){
Ti . Platform . openURL ( obj . webUrl );
} else {
if ( Titanium . Platform . canOpenURL ( obj . appUrl )) { Ti . Platform . openURL ( obj . appUrl );
} else {
Ti . Platform . openURL ( obj . webUrl );
}
}
};
As you can see from the code above, on Android we dont check for anything. The reason
for this is because Android will automatically open the App if it is already installed;
otherwise itll fallback to open the web browser. On iOS however, we have the
canOpenURL function, to which we send the App URL Scheme, and iOS will return True
if the App URL URL was reached. We then react accordingly by calling the openURL
function with either the appUrl or the webUrl.
To learn more about App URL Schemes, you can refer to http: //handleopenurl.com/. To
learn more about how to use them from Titanium, you can refer to
http://fokkezb.nl/2013/08/26/ url-schemes-for-ios-and-android-1/
Weve finished our Conversation tab. In this tab we learned how to connect to Twitter and
pull a list of tweets based on a given hashtag and add them to a ListView. We saw how
iOS Toolbar runs in the same context as the Window, so adding the refresh button was
straightforward. On Android however the ActionBar belongs to the TabGroup, so we had
to implement the icon switching at the TabGroup level. Finally we implemented an event
listener on the ListView, from which we grabbed the tweet id, constructed a Twitter URL
and called a function that will open the Twitter app if the app is installed or the web
browser if no app was found.
Figure 6.12:
Expected results for Video Window
We start with the markup for our view, which is very similar to the Conversation window
< Alloy >
< Window id = video < RightNavButton class = container >
platform = ios >
< Button systemButton = Ti . UI . iPhone . SystemButton . REFRESH onClick = dorefresh / >
</ RightNavButton >
< ListView id = videolist onItemclick = doclick >
< Templates >
< ItemTemplate
< ImageView name = rowtemplate class = rowtemplate > bindId = thumb class = thumb / >
< Label bindId = summary
< Label bindId = author
class = summary / >
class = author / >
< Label bindId = duration class = duration / > </ ItemTemplate >
< ItemTemplate name = loadingtemplate class =
loadingtemplate >
< Label bindId = loading class = loading / >
</ ItemTemplate >
</ Templates > < ListSection < ListSection
</ ListView > </ Window >
</ Alloy >
name = notification / > name = videos / >
Just like we did with Twitter, well use the socialfeeds.js module, to which we need to add
a couple lines of code to make it work with YouTube. At the time of this writing, YouTube
requires no special library, API keys or authentication, at least not for reading data.
Connecting to Web Services and consuming data from them is something youll need to
do at one time or another. The process of connecting to YouTube is in fact no different to
that of communicating with any Web Service to which youd need to connect via HTTP.
Appcelerator provides an object called HTTPClient, which allows you to connect to any
web service via HTTP Protocol.
All we need to do is go to your getFeed function an extra case for YouTube.
case YOUTUBE :
var _url = https :// gdata . youtube . com / feeds / api / users /# USER #/ uploads ? max - results =# MAX #& alt = json ;
var url = _url ;
url = url . replace ( # USER # , Ti . Network . encodeURIComponent ( args . user )) ;
if ( args . max >0) {
url = url . replace ( # MAX #, args . max ) ;
} else {
url = url . replace ( # MAX # , 20 ) ;
}
var http = Ti . Network . createHTTPClient ({ onload : args . success ,
onerror : args . error
})
http . open ( GET , url ); http . send () ;
This function was fairly easy to implement because its following the same architecture we
laid out before. Just like before, we want to make it easy to change data going forward, so
lets add the YouTube user as a global variable in config.json.
global : {
venueAddress : 747 Howard St , San Francisco , CA
94103 ,
venuePhoneNumber : + (415) 974 -4000 ,
twitterHashTag : # tidev ,
twitterConsumerKey : the_given_key ,
twitterConsumerSecret : the_given_secret ,
youtubeUser : appcelerator
}
Now that we have our socialfeeds.js function working with YouTube, lets create a new
module for our videos. Create an empty file in /lib named videomod.js. This file will have
the exact same structure as conversationmod.js. We start with the refreshList function. In
this case were calling YouTube for a specific user, so well specify the YOUTUBE type.
function refreshList ( tableObject ){
require ( socialfeeds ) . getFeed ({
type : YOUTUBE ,
user : Alloy . CFG . youtubeUser ,
max : 20 ,
success : function (e ){
fillTable ( this . responseText , tableObject );
},
error : function (e ){
console . log ( this . responseText ) ; }
})
}
The fillTable function, again, is very similar to the one used for Twitter.
function fillTable ( response , tableObject ) {
try {
var data =[];
var parsed = JSON . parse ( response );
parsed . feed . entry . forEach ( function ( video ){
var link = var summary = var author = var duration = var thumb =
;
var row ={
link : summary : author : duration : thumb :
}
data . push ( row ); video . link [0]. href ;
video . title . $t ;
video . author [0]. name . $t ;
video . media$group . yt$duration . seconds ; video . media$group . media$thumbnail [0]. url
link ,
summary ,
author ,
convertMS ( duration *1000) , thumb
})
var listItems = _. map ( data , return {
thumb : summary : author : duration :
text : item . duration .h + : + item . duration .m + : + item . duration .s },
properties : { url : item . link }
};
}) ;
function ( item ) {
{ image : item . thumb }, { text : item . summary }, { text : item . author } , {
tableObject . sections [1]. setItems ( listItems ); if ( tableObject . sections [0]. items . length >0) { tableObject . sections [0]. deleteItemsAt (0 ,1) ;
}
} catch ( e){
alert (L( offline_error ) );
}
YouTube returns video duration in milliseconds. To convert it into a friendlier format, Ill
used a function I call convertMS, which will essentially take the integer number that
represents milliseconds, will perform several math functions on top of it, and will return
an object with values for days, hours, minutes and seconds.
function convertMS ( ms ) {
var d , h , m , s ;
s = Math . floor ( ms / 1000) ;
m = Math . floor (s / 60) ;
s = s % 60;
if ( parseInt (s ) <10) s = 0 + s;
h = Math . floor (m / 60) ;
m = m % 60;
if ( parseInt (m ) <10) m = 0 + m ;
d = Math . floor (h / 24) ;
h = h % 24;
return {
d : d , h : h , m : m , s : s
};
};
Just like we did before, we can use jsoneditoronline.org to inspect the data returned by
YouTube. Figure 6.13 shows where the data is located within the response object.
We then perform the transformation with the map function, and get a ListView friendly
object as seen below.
{
thumb : {
image : https :// i. ytimg . com / vi / ur00xDMMmNU /0. jpg },
summary : {
text : Making the Mobile Mind Shift : A Forrester },
author : {
text : Appcelerator
},
duration : {
text : 0:54:56
},
properties : {
url : https :// www . youtube . com / watch ?v = ur00xDmNU & feature = youtube_gdata
}
}
Figure 6.14:
Relationship for transformed object and ListView markup
Now to call these functions from the videos.js controller, we do as before.
function dorefresh ( evt ){
loadVideos () ;
function loadVideos () {
if ( Ti . Network . online ){
if ($ . videolist . sections [0]. items . length ===0) {
$. videolist . sections [0]. insertItemsAt (0 ,[{ template : loadingtemplate , loading :{ text :L( refreshing ) }
}])
require ( videomod ) . refreshList ($. videolist ) ; }
} else {
alert (L( offline_error ) );
}
}
loadVideos () ;
if ( Ti . Network . online ){
if ($ . videosTab . videolist . sections [0]. items . length ===0) {
$. videosTab . videolist . sections [0]. insertItemsAt
(0 ,[{
template : loadingtemplate ,
loading :{ text :L( refreshing ) }
}])
require ( videomod ) . refreshList ($. videosTab . videolist ) ;
}
} else {
alert (L( offline_error ) );
}
}) ;
break ;
This completes our Videos tab, the last piece of our Conference app congratulations!. Just
like I mentioned before, Im not including the stylesheets, as they have nothing special
except positioning and styling of the row elements. Make sure you check them in the
companion source code.
6.4 Summary
This chapter concludes the code for the Conference app we started in chapter 4. In the
process weve worked several important topics that will apply to all your apps.
Changing ActionBar menu times on tab change
When working with single-window apps on Android, its easy to add ActionBar menu
items by using the <Menu >tag. When working with Tabs however, the 6.4. Summary
ActionBar doesnt belong to the individual window, but to the tab group. In order to
change icons, we need to set an event listener to the focus event of the tab group, and from
this event grab the currently select tab. Then using this tab number, we can add menu
items to the ActionBar, first calling the invalidateOptionsMenu, and then setting a
callback on the onCreateOptionsMenu method of the Android Activity.
ListViews
The ListView component was designed to overcome the performance issues that could rise
from creating complex TableViews. With ListViews, you first create ListView Templates
that define the contents of each row, and then create an Array of data that includes the
template to be used by each row. Underneath, Titanium will request all the rows at once
from the native SDK, resulting in a dramatic increase of your lists performance.
Connecting to Twitter
Twitter has a very strict security model, which requires you to formally create a Twitter
app and obtain API keys that will allow you to connect to the service. Since implementing
the necessary secure connections is not trivial, we used the codebird.js third-party library,
connected to Twitter, obtained the data in JSON format, which we then used to display in
a ListView.
Web Services
Generally speaking, web service consumption starts with an HTTP call. In this chapter we
consumed data from YouTube, using Titaniums HTTPClient Object. From this library we
called YouTube, received the JSON response, we parsed it and displayed it using a
ListView.
Opening other apps
We explored briefly the concept of App URL Schemes, and we used it to open the native
Twitter app. We also saw how all this is done by using Titaniums openURL method,
which not only works for opening other apps, but also to open a web browser pointing to a
desired page.
In chapter 7 well build an app that uses the devices camera, takes a picture, performs
some changes to it, and then allows you to share it with social networks.
Chapter 7
Just like weve done in previous chapters, all styling and positioning details are in the
stylesheet, giving you the flexibility of changing them without disrupting the actual screen
markup. Listing 7.2 shows index.tss.
Listing 7.2: index.tss
. container [ platform = ios ]: {
orientationModes : [ Ti . UI . PORTRAIT ],
backgroundColor :#282828 ,
statusBarStyle : Titanium . UI . iPhone . StatusBar . LIGHT_CONTENT }
. container [ platform = android ]: { orientationModes : [ Ti . UI . PORTRAIT ], backgroundColor :#282828
}
# start :{
height :300 ,
width :300 ,
backgroundImage :/ photobtn . png ,
backgroundSelectedImage :/ photobtn_dn . png
}
# portrait_tip :{
text : L( portrait_tip ) ,
width : Ti . UI . FILL ,
height : Ti . UI . SIZE ,
textAlign : Ti . UI . TEXT_ALIGNMENT_CENTER , color :# fff ,
bottom :20
}
# info :{
title : Info ,
showAsAction : Ti . Android . SHOW_AS_ACTION_ALWAYS , icon : Ti . Android . R. drawable . ic_menu_help
}
. helpbutton :{ top :30 , right :5 , backgroundImage :/ images / helpicon . png , width :30 ,
height :30 }
Make sure you check the companion code for the graphical assets used for the help icon
on iOS and the big button in the center, for which we have two versions: pressed and
unpressed.
This example is showing the app as it was submitted to the App Store and Play Store,
which use the help icon to promote this book. When you build the app for yourself, you
wont need the bookurl variable or any other reference.
Now, open your index.js and lets start creating the methods we need; well start with the
easiest one, the showinfo callback executed when the user clicks on the help icon.
function showinfo ( evt ){
var dialog = Ti . UI . createAlertDialog ({
message : String . format (L ( about ) , Alloy . Globals . bookurl ) , cancel : 1,
buttonNames : [L( book_btn ) , title : L ( about_title )
})
dialog . addEventListener ( click , if (e . index === 0) {
Ti . Platform . openURL ( Alloy . Globals . bookurl ); }
}) ;
dialog . show () ;
L( close_btn ) ],
function (e ){
The code above calls the Titanium showCamera method, which is a crossplatform
function that shows the camera and returns the picture that was taken. The usage of this
function is simple and straightforward. We however need to take care of one important
Android detail, which you can see in the line that calls the rotateAndResize method.
The problem were solving is that because of the diversity of Android devices, there are
some obscure ones that do not properly return the data that specifies the orientation in
which the photo was taken. On top of this, newer Android devices take pictures in such
high resolution, that the file size makes the device go out of memory when trying to
display it on the screen. At the time of this writing, heres no Titanium-provided
mechanism to fix the orientation problem, so well be using a native module, which well
access through the wrapper function shown in listing 7.4. If in the future Titanium
provides this functionality, simply change the line that calls the imgfix so it is just like the
iOS one.
Listing 7.4: imgfix.js
function rotateAndResize ( media , width , quality ) {
var moment
var utilsModule
= require ( alloy / moment ) ;
= require ( com . alcoapps . imageutility ) ;
var dataDirectory
() ;
var fileName
= Ti . Filesystem . getApplicationDataDirectory
= String . format ( Company_Photo_ % s. jpg , moment () . format ( YYYY -MM -DD -HH - mm -ss - SSS -ZZ ) ); var file
fileName ) ;
var fileNativePath = Ti . Filesystem . getFile ( dataDirectory ,
= file . nativePath ;
file . write ( media ); file = null ;
utilsModule . rotateResizeImage ( fileNativePath , width || 640 , quality || 80) ;
media = Ti . Filesystem . getFile ( fileNativePath );
return media ; exports . rotateAndResize = rotateAndResize ;
The rotateAndResize function receives the original photo, the desired destination width
and the desired compression ratio. This function is a wrapper that simply creates a
temporary file handle, which is then sent to the native module, which takes care of all the
complex details on the native level. After the module has run, the new image is returned to
the caller. The native module is free and open source, and can be downloaded from
https://github.com/ricardoalcocer/AndroidRotateImage and placed in your modules folder,
and the imgfix.js file should live in your /lib folder.
Now that we have created our modules for taking and fixing the picture, were ready to go
back to index.js and implement the doClick callback that is executed when the camera
button is pressed.
var cb_success = function ( img ){
Alloy . createController ( edit ,{ image : img }) . getView () . open () ; }
var cb_fail = function ( err ){
if ( err . code == Titanium . Media . NO_CAMERA ) { console . log ( No Camera ) ;
} else {
console . log ( Fail ) ;
}
}
function doClick ( e) {
require ( photomod ) . takePhoto ( cb_success , cb_fail ) ;
}
$. index . open () ;
In the code above we define the two callbacks, required by the takePhoto method we
created earlier. Once the picture was taken successfully, the cb success function will
receive the image, which will be sent to the edit controller and the edit window will be
opened.
Notice that weve added the ActionBar for Android and a Toolbar for iOS. This will
provide the native structure needed to share the image and close the window, as shown in
Figure 7.10.
For the background color Im using an 8 digit HEX number instead of the regular 6, or 3
depending on the case. This is actually a transparent black, so the last 6 digits are in fact
the normal representation of black, 000000. The first two digits, in this case 40, represent
20% of opacity, causing the background to be shown transparent. This is called ARGB
(Alpha, Red, Green, Blue), providing 2 digits for the Alpha channel, 2 for Red, two for
Green and two for blue. The opacity has to be provided as HEX values, so see the Table
7.1 for possible opacity values. This is the recommended crossplatform way of applying
transparent colors to Titanium.
100% FF 45% 73
95% F2 40% 66
90% E6 35% 59
85% D9 30% 4D
80% CC 25% 40
75% BF 20% 33
70% B3 15% 26
65% A6 10% 1A
60% 99 5% 0D
55% 8C 0% 00
50% 80
Table 7.1: HEX Opacity Values Background colors can be provided as either HEX triplets
or Red, Green and Blue sets like rgb(255,0,0). If using the latter, you could specify
transparency by calling rgba(255,0,0,1.5), where the last number is the value for the Alpha
Channel. Its important to keep in mind however that should you use rgba, the Alpha
Channel on iOS is expressed as a number between 0 and 1, while on Android is expressed
as a number between 1 and 255.
Also in that snippet you can see that Im using a custom font, Impact. Using custom fonts
is as easy as simply naming them in you stylesheet. Youll have to ship that font with your
app though, and you do this by creating a folder named font inside your assets folder, as
seen in Figure 7.11.
Figure 7.11:
Custom fonts
Add your custom fonts to this folder, keeping in mind that theyll increase the apps file
size. For more information about using custom fonts, refer to the official documentation at
http://docs.appcelerator.com/titanium/3.0/#! /guide/Custom_Fonts.
Once we have our markup and styling, lets start working on the controller that
implements the functionality for this window.
shown below.
var args = arguments [0] || {};
var thisWin = $ . edit ;
$. byline . text = Alloy . Globals . byline ;
$. thephoto . image = args . image ;
function doclose () {
thisWin . close () ;
}
In the code above we create a variable to hold a pointer to this window for easier access
later. We also grab the byline from the Alloy globals and the photo that came as payload
from the previous window. We also implement the first method, the close button, which
simply closes the Window.
Next, lets take care of editing the text, which should work as seen in Figure 7.12.
Figure 7.12:
Editable areas and dialog boxes
If you remember from edit.xml, we added click events to the text labels. < Label id = memetoptext
class = caption onClick = edittop / > < Label id = memebottomtext class = caption onClick = editbottom / >
These two callbacks will open the dialogs that will be used to edit the text. However, the
way these dialogs are constructed differs between platforms, so well be creating a
CommonJS module that will wrap this functionality for us, allowing us to keep the code
easy to read, as seen below.
function edittop ( evt ){
require ( editdialog ) . show ({
hint : L( enter_text ) ,
closeButton : L ( close_btn ) ,
callback : function ( text ){
$. memetoptext . text = text ;
}
}) ;
}
function editbottom ( evt ){
require ( editdialog ) . show ({
hint : L( enter_text ) ,
closeButton : L( close_btn ) ,
callback : function ( text ){
$. memebottomtext . text = text ; }
}) ;
}
As you can see from the code above, the editdialog module, which well be building
shortly, will make your code clean and simplified, leaving all the platform-specific code
out of the main logic of your controller. By using this technique, youre actually
increasing your code reusability, as your app code remains constant, and all platform-
specific changes are handled by an external module. Lets look at how to build this
module.
This code will effectively display the dialog on iOS showing a text field and the specified
buttons. At the moment of executing the event listener, the text that was entered will be
returned to us in the text property of the event object, in this example e.text. Lets now
look at the Android version of this dialog.
var textfield = Ti . UI . createTextField ({
height : Ti . UI . FILL ,
width : Ti . UI . FILL
}) ;
var dialog = Ti . UI . createAlertDialog ({
title : DESIRED TITLE ,
androidView : textfield ,
buttonNames : [ ARRAY ,OF , BUTTON , TITLES ] ,
cancel : 1
}) ;
dialog . addEventListener ( click , function (e ){
if (e . index !== e. source . cancel ){
}
}) ;
dialog . show () ;
In the Android version above, we need to create a TextField and then add it to the
androidView property of the AlertDialog object, which will effectively embed this text
field into the dialog. When the event listener is executed, the value will be returned to us
in the value property of object that was provided as androidView, in this example
e.source.androidView.value.
This code could easily be added to the controller in an if block. However, I always try to
create modules for platform-specific code like this one for two reasons: first, the code ends
up being more readable, and second because it allows me to perform any additional
change in the module without actually touching my controller code. This is however a
decision that has to do with your coding style, and has no real effect in how Titanium
behaves.
Now that you know how the iOS and Android dialogs work, lets look at how we want to
be able to call them. We want to be able to make the same call regardless of the platform,
for example:
require ( editdialog ) . show ({
hint : L( enter_text ) ,
closeButton : L ( close_btn ) ,
callback : function ( text ){
$. memetoptext . text = text ; }
}) ;
Listing 7.6 shows the integrated module, which you should save to /lib. The main
function, called show, receives a variable named args, which is an object that contains
the properties: hint, closeButton and callback. Once it runs, itll take care of the rest.
Listing 7.6: editdialog.js
function show ( args ){
if ( OS_IOS ){
var dialog = Ti . UI . createAlertDialog ({
title : args . hint ,
style : Ti . UI . iPhone . AlertDialogStyle .
PLAIN_TEXT_INPUT ,
buttonNames : [ args . closeButton ,OK ] , cancel : 0
}) ;
dialog . addEventListener ( click , function (e ){ if (e . index !== e. source . cancel ){
args . callback (e. text ) ;
}
}) ;
dialog . show () ;
} else {
var textfield = Ti . UI . createTextField ({
height : Ti . UI . FILL ,
width : Ti . UI . FILL
}) ;
var dialog = Ti . UI . createAlertDialog ({
title : args . hint ,
androidView : textfield ,
buttonNames : [ OK , args . closeButton ],
cancel : 1
}) ;
dialog . addEventListener ( click , function (e ){
if (e . index !== e. source . cancel ){
args . callback (e. source . androidView . value ); }
}) ;
dialog . show () ;
}
}
exports . show = show ;
Weve implemented the text editing and the close button of this window.
Whats left to implement is the sharing action.
If youve used Android before, youve seen the Share Intent dialog in Figure 7.13.
The above code snippet takes care grabbing the given text and image, and sharing it with
the app the user selects from the sharing dialog. Well be using this code in a module that
well create shortly, but first, lets see how this works on iOS.
iOS has a native object called UIActivityViewController which is equivalent to Androids
share intent dialog. The Titanium core SDK however doesnt currently expose it, so in
order to use it we need to rely on a module. The module well be using is free and Open
Source, and can be found at https: //github.com/viezel/TiSocial.Framework. Go ahead and
download the latest version and save it to your /modules folder, just like you did in
Chapter 4 with the ViewPager module. Dont forget to also add it to your tiapp.xml as:
< modules >
< module platform = ios > dk . napp . social </ module >
</ modules >
Now, just like we did with the editdialog, I have created a CommonJS module that wraps
both the Android Sharing Intent and the TiSocial.Framework module. The module is
called com.alcoapps.socialshare and can be downloaded from
https://github.com/ricardoalcocer/socialshare. Download it and drop it in your /lib folder.
Now that weve explored the details of how to share text and images with social networks
and we have all of our modules in place, were almost ready to implement the function
that actually shares our meme. Before we get to it, we need to add another third-party
module, this one in a form of an Alloy Widget. If you remember from chapter 3 where we
looked at Alloy Widgets, there are different sources to find pre-made Widgets to be used
in your apps. In this app well be using a Widget called nl.fokkezb.loading. This Widget
will provide us with a nice progress dialog to be used in the iOS version of our app.
Download the Widget from https://github.com/FokkeZB/nl.fokkezb.
loading,andsaveittoapp/widgets. Then remember to open your config.json file and add the
Widget as a dependency.
dependencies : {
nl . fokkezb . loading :*
}
require ( com . alcoapps . socialshare ) . share ({ status
image
androidDialogTitle })
Having done this, go to index.js and add the following line to the top of the controller.
Alloy . Globals . loading = Alloy . createWidget ( nl . fokkezb . loading ) ;
This line runs as soon as you run your app, and creates a global instance of the widget, so
it can be used from any screen. Well be using this Widget for our iOS version, because as
you remember, in our edit.xml file we added a tag for the Android Progress Indicator with
the line seen below.
< ProgressIndicator ns = Ti . UI . Android id = progressIndicator platform = android / >
We are now ready to create our function to share the image, which well add to edit.js.
function btnshare ( evt ){
var meme = fileToShare = null ;
if ( OS_ANDROID ){
$. progressIndicator . show () ;
} else {
Alloy . Globals . loading . show (L ( generating ) , false ) ; }
setTimeout ( function () {
if ( OS_ANDROID ){
meme = $. meme . toImage () . media ;
fileToShare = Titanium . Filesystem . getFile ( Titanium .
Filesystem . externalStorageDirectory , tmpmeme . jpg ) ; } else {
meme = $. meme . toImage () ;
fileToShare = Titanium . Filesystem . getFile ( Titanium .
Filesystem . applicationDataDirectory , tmpmeme . jpg ) ; }
fileToShare . write ( meme ) ;
if ( OS_ANDROID ){ : L ( signature ) ,
: fileToShare . nativePath , : L( android_share_dialog )
$. progressIndicator . hide () ; } else {
Alloy . Globals . loading . hide () ; }
} ,200)
}
In the code above you can see that we are conditionally showing and hiding the progress
indicators depending on the platform, effectively showing native progress bars as seen in
Figure 7.14.
Figure 7.14:
Platform-specific progress indicators
Were then wrapping our code in a JavaScript setTimeout function, configured for 200
milliseconds. The reason for doing this is that the toImage function runs synchronously,
potentially blocking the UI for a couple ticks. If we run toImage right after showing the
progress indicators, we risk the UI being briefly blocked before the indicator is shown,
causing that we dont see it at all.
We then make sure we get the right image, because on Android the image is returned in
the media property. We write the image to a temporary file, and then call the share method
to which we send the nativePath to the temporary file. By running the share method, the
module will show the native sharing window for the platform in which were running, as
seen in Figure 7.15.
Figure 7.17:
Default icons and splash screens for iOS and Android
To create your app icons and splash screens, simply open each of this files, replace the
contents with your own images, and save with the same name in the same location. Its a
time consuming process but will ensure that your images are appropriate for each device.
Luckily, and again thanks to the active developer community, theres a tool called TiCons
(Titanium Icons) that can do all the hard work for you. TiCons comes in two flavors:
Command-Line interface or Website. Ill show you how to use the website to generate all
the images you need for your app.
To begin, youll only need to create 3 files.
The first file will be used as a base for all your iOS icons and should be a 1024x1024
pixels png.
The second one will be used as the base for your Android icons and should be a 512x512
pixels transparent png.
Finally, and arguably the most important one, is the base file for all you different splash
screens. This file should be a 2208x2208 pixels png, but the content itself should be kept
in the inner 1000x1000 box, allowing enough slack around for cutting landscape and
portrait images.
Once you have these images, head over to the TiCons website at http: //ticons.fokkezb.nl/
and select these 3 images in their appropriate locations, as seen in Figure 7.18.
7.4 Summary
In this chapter we built the last app of the book. We learned a couple interesting things
here.
Taking Pictures
We saw how to take pictures using Titanium.Media.showCamera(). We also saw how
there are certain Android devices that could return pictures with incorrect orientation
information, and others that return images in extremely high resolution. For these two
cases we used a third-party module, which we wrapped in a CommonJS class for easy
implementation.
Custom fonts and transparent view backgrounds
We also learned how to use custom fonts, apply transparent backgrounds, add them to
images taken with the camera and merging all the objects together and create a single
image out of them. We saw how to use CommonJS to normalize platform-specific
function calls, making your code much more readable and easier to maintain.
Sharing images with social networks
We then explored the specifics of sharing text and images with social networks. We saw
Android Intents and how they are provided by the operating system, and used a third-party
module to access a similar functionality on iOS. To provide more consistency in the code,
we accessed these two features by using a single function call exposed by another module.
Creating Splash Screens and App Icons
Last but not least, we saw the diversity of images that need to be created for app icons and
splash screens in order to account for all the different device sizes and screen densities,
and used the TiCons online tool so save us hours of graphic design work.
This concludes the code for the book. However, Ill still want to share some thoughts with
you. In Chapter 8 Ill give you my Top 10 Tips for Successful Cross-platform
development. Ive used these tips in national and international speaking engagements and
for this book Im making them available as a highresolution Infographic, which you could
print and have it always handy.
Chapter 8
reusability.
8.2 Code reuse is for your apps logic, not necessarily for UI
code
There are many misconceptions about code reuse with Appcelerator, or any cross-platform
tool for that matter. Expecting 100% code reuse not only represents little control over your
apps UI/UX, but also most likely means that your app will look exactly the same across
all target platforms. Some people think this is the ultimate goal of a cross-platform tool,
but thats wrong: thats the goal of a web browser. Reusability however is achievable with
your apps logic, as we saw particularly in Chapter 7, were we created CommonJS
modules for the Edit Dialog and Social Sharing. These modules acted as normalizers for
platform-specific functions, thus increasing the percentage or reused code. 8.3. Always
think web service
I admit its not easy: it took me a while, as a former web developer, to recognize and
accept this, but take it from me, its worth the effort, and youre apps will be better and
better. To help you even more in your journey, I created an app template you can use every
time you create a new app, seen in Figure 8.2.
Twitter writes its software, what type of database server theyre using or the structure in
which theyre storing their data. In fact, the only thing we know is what they want us to
know, what is given to us through the Twitter API. The Twitter API is a set of abstract web
services that were designed to provide access to the internals of Twitter, regardless of how
youre going to use the data, and even when this sounds limiting, you can in fact build a
full Twitter client using only their web services.
Since were developing cross-platform apps, I recommend you build your code
completely abstract, just as if it was a web service: a local web service if you like. Then on
top of this, build the native user interfaces, which are consumers of our local web
services. Using this architecture you can make sure that all the user interface code is
completely decoupled from the logic, and both can flow independently. Whats more, it
really will not make that much of a difference if youre using a TabbedBar on iOS and a
ViewPager on Android (like we did in Chapter 4), because after all, the user interface code
is simply a consumer of your application logic.
android.com/design/index.html.
After reading through these documents, youll have a wealth of information that will help
you to understand each platform, design better user interfaces and know what to look for
in the Titanium API, or alternatively as a third-party module.
to then run it on iOS and realize the object positions are off, fonts need adjusting, or your
app is simply crashing on load. My recommendation is to test often, so if something
breaks on one platform, you can immediately know whats the problem related to.
8.11 Summary
These are my Top 10 Tips for Cross-platform mobile development using Appcelerator.
Ive made these tips available as an Infographic, seen in Figure 8.5.
Figure 8.5:
Infographic
Im making this Infographic available to readers of this book as a hi-resolution JPEG from
this link: http://bit.ly/tialloyinfographiclg. The image was 8.11. Summary
designed in poster size, so feel free to print it out if youd like - Im sure Ill look
awesome on your wall.
Appendix A
A.1.2 Comments
You can write comment lines using double slashes // and comment blocks using /* and */.
See below.
// this is a commment
/* this is
also a comment */
memory. To assign a value to a variable you use a single equal (=) sign.
Variable scope, or where you can access variables from is a bit tricky in JavaScript so Ill
explain using some examples.
var a =1;
function one () {
alert (a);
}
one () ;
The result of running this script is a message box with the number 1, proving that variable
a is global and accessible from inside the function even when it was created outside of it.
var a =1;
function two (a){
alert (a);
}
two (100) ;
In this case, when we call function two it will display the number 100 because its
displaying the value of the variable a that is local to the function. They both can be named
a and wont conflict with one another because they belong to different scopes.
var a =1;
function three () {
var a = 3;
alert (a);
}
three () ;
This case is exactly the same one as before, but no value was sent to the function, resulting
in displaying the number 3.
function saySomething () {
var message = This JavaScript thing is cool ;
alert ( The message is : + message ) ;
}
saySomething () ;
alert ( The message is : + message ) ;
Finally in this example well get en error when trying to display the variable message from
outside the function, as this variable belongs to the function and is not accessible to
anyone outside its local scope.
Number
var price =100;
Boolean
var completed = false ;
Array
var colors =[ Red , White , Green ]; var values =[100 ,200 ,300];
Object
var employee ={
name : Steve ,
department : Engineering
}
Null
An actual value that means empty
Undefined
The value of a variable that has no value
x != 10 // returns true
// for x =2
\ for x =2
x <= 10 // returns true
OR operator (||)
A.2 Functions
Functions are a very important part of JavaScript. With a JavaScript function you can
accomplish from basic things like adding two numbers, to creating a complete class that
provides you access to the Twitter API.
At this point variable total has the result of the function, that is, the number 10.
If your self-invoking function needs to receive arguments, youll format it like this.
function ( arg1 , arg2 ){
// perform
// your operations
// here
}( val1 , val2 )
Any variables inside this function belong to the function, and are not available to the rest
of your program.
A.3 Arrays
The above example is the traditional way of building arrays in JavaScript. However the
recommended way is using Array Literals.
var colors =[ Red , White , Green ];
A.4 Objects
In JavaScript youre always working with Objects. When you create a variable of type
String, it is actually an object of type String, which automatically inherits a set of
properties and methods.
var carmake = Honda ;
alert ( carmake . toUpperCase () ) ;
toUpperCase is a method inside the String object that will automatically convert your
String to upper case.
You can create your own objects using the object literal syntax.
var person ={
name : jack ,
email : jack@ctu . com ,
twitter : @jackb_ctu
};
alert ( person . name );
];
alert ( arr [0]. key [1]) ;
Notice in this previous example that when you run the function, what youre actually
doing is building an object with two methods: addOne and getTotal. These two methods
are accessing the value of the myCount variable, which is local to the main function, and
even after the function returns, you not only can get the value of the variable by calling
getTotal, but you can also change its value by calling addOne. This pattern is called the
Yahoo Module Pattern. JavaScript closures can be difficult to understand at first, but use
them and theyll become second nature.
A.7 JSON
JSON stands for JavaScript Object Notation and its a text-based data interchange open
standard based on JavaScript objects and arrays. JSON is sometimes considered a light
weight version of XML, in the sense that it serves the same data interchange purpose, but
its smaller in size. JSON is an ideal format to send data from and to mobile devices
because the data packets are often small (as compared to XML), thus in the end protecting
the users data plan.
A.7. JSON
Today theres a JSON implementation for virtually any programming language, so if your
App reads JSON, it will be capable of interchanging data with almost any web service.
The format of a JSON string is very similar to Arrays and Object literals. Consider the
following example.
[ { id : 1,
name : Apple ,
url : http :// apple . com , tags : [ iphone , ipad ]
},
{
id : 2,
name : Android ,
url : http :// android . com , tags : [ googletv , chromecast ]
}
]
Notice that at plain sight it looks exactly the same as an Array literal with two object
literals as elements. The main difference is that the properties are strings (notice the
double quotes in the property names. This is a JSON requirement and they have to be
double quotation marks. Other than that, its like working with JavaScript objects.
A.9 Summary
In this appendix, you saw
The importance of understanding the JavaScript language
Different ways of building functions
Using array and object literals
Using functions to return objects
How JSON works and its importance in mobile applications
Appendix B
could be features of the Titanium CLI only available starting on specific versions of the
Titanium SDK.
Titanium Studio
Titanium Studio is Appcelerators IDE (Integrated development environment). Titanium
Studio uses the Titanium CLI, the Alloy CLI and the Titanium SDKs, plus provides
additional features like syntax highlighting, debugging capabilities, SDK management and
project management, among many others.
It can be used on its own if youd rather not use Titanium Studio. From the Titanium CLI
you can also examine your complete Titanium installation and has configuration tools that
can help you figure out configuration problems and conflicts. For a full syntax reference
refer to http://docs.appcelerator.com/titanium/3.0/#!/guide/Titanium_ CommandLine_Interface_Reference
B.1.5 Node.js
Node.js is a tool designed for taking JavaScript out of the browser and bringing it to the
server. The Titanium CLI and Alloy are written in Node.js and for that reason it is
required. If at some point you run into problems that appear to be related to the Node.js
installation, refer to the guide located at
https://wiki.appcelerator.org/display/guides2/Installing+Node
B.1.6 Git
Git is a tool for managing collaborative development. Youve perhaps heard of Github, but
the two are different. Git is used to manage you software off-line, while Github is a place
you can use to store your Git-managed projects on the web. For information about
installing Git refer to https://wiki. appcelerator.org/display/guides2/Installing+Git
B.1.7 Genymotion
Genymotion is a set of Android emulators based on virtualized Android machines hosted
on VirtualBox. As of Titanium 3.2 Genymotion is supported from the Titanium CLI, and
as of 3.3 officially supported by Titanium Studio. Genymotion can be obtained from
http://www.genymotion.com/, is free for personal used and requires a subscription for any
other use. For additional installation details refer to
http://docs.appcelerator.com/titanium/3.0/ #!/guide/Installing_Genymotion
seen in Figure B.3 If this is your first run, I suggest you install every update available. In
the future however, if youre prompted to install updates, I suggest you carefully read
what is being updated and decide whether or not you need the update at this point.
Installing updates without paying attention to whats actually being updated may result in
breaking existing apps.
B.2.2 Configuring
Studio will now launch the Platform Configuration dialog, shown in Figure B.4, which
will guide you in the process of installing the SDKs for the platforms you wish to develop
for.
Figure B.6:
Android configuration
On the Android side, first the Android SDK Manger is downloaded and the each
individual SDK version you selected, as seen on Figure B.6. You dont need to download
all Android versions, only the ones you want to develop and test for. If youre not sure,
download the most recent one. You can always come back to the screen and download
additional ones.
After everything has been downloaded and configured, youll see that Titanium Studio
will show a green checkmark next to the SDKs that have been properly installed and
configured, as you can see in Figure B.7.
Appendix C
File/path/name is the name of the file that will be created. The alias is a friendly name for
this file. If your app is called invoices, you could us the alias invoiceskey. Once you
hit enter youll be prompted with some information about you, which will be used to
create the digital signature. Youll then be prompted for a password and the file will be
created.
To verify that the file was properly created and has the right data, you could use the
following command.
keytool - list -v - keystore < file / path / name >
After creating your Key, youre ready to compile and sign your app with it. Open up
Studio with your project and from the toolbar select Package from the Run drop down
menu, as seen in Figure C.1.
Figure C.5:
Android Developer Console
Click on Publish an Android App on Google Play and youll be prompted for the
information in Figure C.6.
Figure C.16:
Validate your app
This is the process that any native iOS developer needs to go through. Some versions of
Appcelerator have shown a warning message after clicking Validate. If you get a warning,
try unchecking the checkbox on the bottom of this screen.
After successfully validating the app youre ready to submit.
Figure C.17 above shows the submission process, which could take a couple minutes
depending on the size of your app and the speed of your Internet connection. Youre now
ready to add this build to your listing.
Go back to iTunes Connect and select your apps listing. Scroll down and select the + sign
next to Build. This will show a popup (Figure C.18) with the file you just submitted
through XCode. Select the build and click Done.
List of Figures
1.1 How the Appcelerator SDK works 6
1.2 Comparison of Classic and Alloy . 9
1.3 Native representation of the Switch control . 9
1.4 Native Switch running on iOS and Android . . 11
1.5 New Project Configuration . 12
1.6 Project Explorer . 12
1.7 Running your app 13
2.1 Relationship between the platform SDKs, the Titanium API and Alloy 16
2.2 How the Titanium API is organized . . 18
2.3 Non-MVC Titanium Code . . 19
2.4 Tight coupling in Titanium Classic 20
2.5 Alloy MVC . . 21
2.6 Titanium Classic and Alloy side-by-side 24
2.7 Anatomy of the XML tag . . 24
2.8 Comparing TSS to CSS 26
2.9 The Alloy dollar sign symbol 28
2.10 Alloy normalizes API calls . . 30
2.11 How our MP3 will look on iOS and Android . 32
2.12 Differences between the iOS Title Bar and Androids ActionBar . 32
2.13 Creating a new mobile project . . 34
2.14 Importing projects into Studio . . 35
2.15 MP3 Player Skeleton . . 37
2.16 Strategy for building the buttons strip . 39
2.17 Main buttons container 40
2.18 Views to hold each button image . 40
2.19 Adding images to buttons 41
2.20 Our current assets and styles folders . . 50 2.21 Add theme folder(s)
51 2.22 Add assets and styles to your theme(s)
. 52 2.23 Alloys position in the Titanium development cycle 53 2.24 MP3
Player with new theme applied . . 56 2.25 Adding additional theme
57 2.26 Change backgroundImage in content class 57
2.27 Change color and position of the controlsbar class 58
3.1 Create a new Alloy Widget . 62
3.2 Enter Widget name . . 63
3.3 Widget folder structure 65
3.4 Widget.xml and Widget.tss side-by-side 66
3.5 Location of widget local assets and styles as compared with global ones
. . 67
3.6 Testing your basic/default widget in iOS and Android . 69
3.7 Widget detached from host app 71
3.8 Create new Alloy Widget . . 72
8.1 Evernote for iOS, Android and Windows Phone . . 226 8.2 Cross-platform
App Template 227 8.3 Facebook notifications window
. . 228 8.4 In-tab navigation on the Facebook app . 229 8.5
Infographic . 232
B.1 First screen after installing Titanium Studio . . 251
B.2 Select Studio Workspace . 251
B.3 Update Titanium Studio components . . 252
B.4 Platform Configuration screen 253
B.5 How to re-launch the Dashboard and Platform Configuration window253
B.6 Android configuration . 254
B.7 Android configuration . 255
B.8 SDK Preferences . 255
C.1 Package application . . 259
C.2 Platforms to package for 259
C.3 Select Android SDK . . 260
C.4 Add key to sign with . . 260
C.5 Android Developer Console . . 261
C.6 Add APK . . 262
C.7 Ready to publish . 263
C.8 Published app 263
C.9 Published app 264
C.10 iTunes Connect Home . 265
C.11 Add app meta data 265
C.12 Fill out details for your app . 266
C.13 Distribute for iOS iTunes Store . . 267
C.14 iTunes Distribution Wizard . 267
C.15 Xcode Organizer . 268
C.16 Validate your app 268
C.17 Submit your app to iTunes Connect . . 269
C.18 Add this build to the app listing . 269
C.19 Submit app for review . 270
C.20 Waiting for review 270
List of Tables
1.1 Native Platform Tools . 7
2.1 Alloy MVC Components 22
2.2 User interface Components required for the MP3 Player 36
2.3 Properties for the main window . . 43
3.1 Contents of the Widget.json file . . 64
6.1 Properties in the args variable 171 7.1 HEX Opacity Values
. . 209
Index
9-patch, 113
absolute path, 75
ActionBar, 33, 38, 46, 49, 102, 167 ActionBar Style, 108, 200
ActionBar Style Generator, 200 ActionBar Styles, 38
actionbar.icon, 107
ActionBarExtras, 116
advanced Android customization, 87 AlertDialog, 213
ALLOY, 8
Alloy, 3, 711, 1317, 2025, 60 Alloy CLI, 63
alloy generate controller, 98
Alloy Models, 116
Alloy View, 24
Alloy.CFG, 55, 122
Alloy.createController, 122
Alloy.createModel, 150
Alloy.Globals, 122
alloy.js, 122
Alpha channel, 209
Android, 35, 13, 116
Android Activity, 178
Android Intents, 215
Android Manifest, 111
Android themes, 87, 108
API, 4, 116
API Keys, 162
App Store, 5, 203
App URL Schemes, 180 app.tss, 59, 133
Appcelerator, 38, 1015, 21 Appcelerators, 4
AppCompat, 109
Apple, 5
Apple App Store, 194
ARGB, 194, 209
args variable, 141
avatar, 93
Backbone, 90, 116, 147 backward compatibility, 109 best practices, 4
Blackberry 10, 4
bridge, 78
canOpenURL, 164
chain, 123
Classic, 3, 811, 15, 16, 19, 20, 23,
30, 31, 60
Clean, 101
CLI, 11, 63, 127
Codebird library, 227
codebird.js, 167
com.companyname.widgetid, 63 comma-separated values, 143 Command Window, 63
CommonJS, 78, 138, 194
config.json, 55, 58, 67, 72, 163 console.log, 154
controller, 61
ConvertCSV, 143
createAlertDialog, 212
cross-platform, 35, 33, 49, 135 CSS, 4, 8, 11, 1517, 22, 40 CSV, 143
custom font, 210
FlexSpace, 38
framework, 4, 5, 8, 15, 16, 19, 60 frameworks, 5, 21
getString, 100
Github, 70, 119 Gitt.io, 70
global settings, 122 global styles, 133 Google Maps, 91 Google Play, 5, 194 grid, 61
grid format, 61
Dark, 39
dark theme, 38
data access, 135 data models, 116 data-binding, 166 databases, 115
debug, 121
decoupled, 122, 228 dependency, 62, 72 detach, 81
Div, 39
DOM, 26
DrawerLayout, 116
Eclipse, 11
Emulating, 91
emulators, 11
encrypted, 147
English, 92
event listeners, 127
exports, 78
extra-extra high-resolution, 103
Facebook, 92, 115, 194 factory methods, 25 Firefox, 142
hardcoded, 132
hashtag, 92, 168
HEX, 209
HEX triplets, 210
HTML, 4, 11, 13, 1517, 20, 22, 24,
39, 40
HTTP, 162
hybrid, 5
marketplace, 119
mdpi, 103
meta-data, 65
mobile strategy, 5
MobileWeb, 4
Models, 135, 147
modules, 116
multi-language support, 87
MVC, 8, 10, 15, 16, 1922, 29, 49,
59, 60
namespace, 1719, 25, 100
native module, 120
Navigation Controller, 155
README.md, 68
refresh, 13
Require tag, 98
res-hdpi, 103
res-ldpi, 103
res-mdpi, 103
res-xhdpi, 103
res-xxhpi, 103
retina, 102
reusability, 68, 118 reusable component, 76 reusable package, 71 reuse, 123
reverse URI format, 63 rotateAndResize, 205
ScrollableView, 127, 128, 163 scrolling list, 115
ScrollView, 131, 156
SDK, 35, 11, 14
Securely, 147
security, 147
Segmented Control, 90, 127, 128 separator, 134
setTimeout, 218
shallow, 155, 228
Share Intent, 194, 215
Sharing Intent, 216
showCamera, 205
simulator, 13
slide-in animation, 156
social networks, 194
Spanish, 92
Splash screen, 220
splash screens, 194
SQL Statements, 152
SQL Strings, 152
SQLite, 89, 135, 142
SQLite Databases, 116
UI, 14, 16, 17, 20, 22, 24, 33, 36, 39, 49
UI/UX, 4, 14, 90, 116
UIActivityViewController, 216
underscore.js, 174
usability, 118
user behavior, 118
user interface, 1417, 20, 24, 61, 92
versioning, 68
View, 22, 24
ViewPager, 90, 116, 118, 228 Views, 23
visual language, 230
web apps, 147
web browser, 162
Web Development, 14 web development, 15 web server, 162
Web Services, 162 Web services, 89
Widget, 61, 216
Widget names, 63 widget.js, 67
widget.json, 63, 68 widget.tss, 75
Window, 20, 23, 123 Windows Phone, 4 WPATH, 75
XML, 8, 15, 21, 2325 xxhdpi, 103
YouTube, 93, 161, 183