[Library] StoreBox, an open-source Android library for streamlining SharedPreferences - IDEs, Libraries, & Programming Tools

When getting a value from any preferences, whether private Activity or default shared preferences, you would normally have to get a reference to a SharedPreferences instance, for example using
Code:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(someContext);
Only after that we can start getting/saving the values, such as
Code:
String username = prefs.getString("username", "");
prefs.edit().putString("username", "androiduser").apply();
This can get tedious if the values need to be saved and retrieved from multiple places as the caller is always required to supply the key of the preference, know under what type it is saved, and know what default should be used. This can also become error prone as more keys get used. Putting commonly accessed preferences behind a wrapper is a reasonable solution, but still requires some boilerplate code.
What if however we could define an interface like
Code:
public interface MyPreferences {
@KeyByString("username")
String getUsername();
@KeyByString("username")
void setUsername(String value);
}
for everyone to use in order to save and get values? The caller doesn't need to worry about the key, neither needs to think about what type the key should use, and the process of retrieving/saving is hidden behind a method name with improved semantics.
With StoreBox that becomes reality. Given the above interface definition you can easily create an instance of the interface using
Code:
MyPreferences prefs = StoreBox.create(context, MyPreferences.class);
and you will be able to retrieve/save the value just by calling the defined methods
Code:
String username = prefs.getUsername();
prefs.setUsername("androiduser");
You can read more about the project here, including details on how it can be used and added to a project.
Let me know what you think and whether you find it useful. Thanks!

great! ty

Great work!

Related

Inflating layout XML files and layout parameters

Why are the layout parm's specified in a layouts XML file not used when you inflate the layout manually in Java. This is a view that is being added to another view.
I have to manually create the layout parms using something like the following after inflating
and before adding to the parent view.
LayoutParams lp = new LayoutParams(-1,-2);
inflatedView.setLayoutParams(lp);
Is there a way to make the newly created view adhere to what is in the XML. I really
hate doing this in Java and divorcing it from the layout's definition.
TIA!
use the LayoutInflater service
Code:
// getSystemService is a method for context and can be called directly from an activity
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myView = inflater.inflate(R.layout.my_layout, null);
Even better, use the
Code:
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
variant with the future parent of the inflated view (with attachToRoot set to false if needed, as required for ListViews) to properly inherit LayoutParams from the parent.
And I simply use LayoutInflater.from(Context) instead of getSystemService(), makes it easy to chain the call :
Code:
LayoutInflater.from(context).inflater(R.layout.my_layout, parent, false);
seydhe said:
Even better, use the
Code:
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
variant with the future parent of the inflated view (with attachToRoot set to false if needed, as required for ListViews) to properly inherit LayoutParams from the parent.
And I simply use LayoutInflater.from(Context) instead of getSystemService(), makes it easy to chain the call :
Code:
LayoutInflater.from(context).inflater(R.layout.my_layout, parent, false);
Click to expand...
Click to collapse
I'm not a fan of chaining calls but that's a style thing. I don't really like the readability/debugability/maintainability of them down the line. But again that's just a subjective opinion, to each their own.
And of course what you posted is pretty readable. But I don't think you really meant to pass an int to the XMLPullParser?
Also why do the API docs say this then...
http://developer.android.com/reference/android/view/LayoutInflater.html
This class is used to instantiate layout XML file into its corresponding View objects. It is never be used directly -- use getLayoutInflater() or getSystemService(String) to retrieve a standard LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are running on. For example:
Code:
LayoutInflater inflater = (LayoutInflater)context.getSystemService
Context.LAYOUT_INFLATER_SERVICE);
Click to expand...
Click to collapse
Is there some danger to using the static LayoutInflater.from(context) ? Or just a badly worded api that is telling us not to instantiate the LayoutInflater ourselves? (I'm guessing the latter).

Trouble with database

Hello all! I'm somewhat new to Android development, I'm using eclipse to do all of my development. Right now I am working on an "away message text" application. I am having trouble with database query returning a string of the first index of data. Here is my code where i am trying to pull data from the database. This is being done inside my ReceiveText class, which extends BroadcastReceiver.
mDbHelper = new TextsDbAdapter(context);
mDbHelper.open();
getText = mDbHelper.fetchText(0);
message = getText.getString(getText.getColumnIndex(mDbHelper.KEY_BODY)).toString();
The error that I am getting is Cursor index out of bounds...any recommendations? Thanks!
you always start out of bounds.... you have to moveToFirst() first...
Thanks. Would I moveToFirst() before I fetchText()?
you're probably going to have to change how the fetchText method is implemented.
you'll probably want something like
Code:
public String fetchText(int id){
Cursor c = getContentResolver().query( uri,
new String[] { <name_of_text_column> } ,
"_id == " + id, null, null);
String text = null;
if( c != null ){
if( c.moveToFirst() ){
text = c.getString( c.getColumnIndex( <name_of_text_column> ) );
}
c.close();
}
return text;
}
Thanks. The method I have for fetchText returns a Cursor, however I made another method that returns a string, similar to what you recommended. For some reason, there is something wrong with the rowid sending in, I want to just get the first one, if I'm not mistaken, wouldn't it be 0? I have a seperate ReceiveText.java class, which extends Broadcast Receiver. The part where the messages are stored into the database is very similar to androids notepad demo. When a text is received, I want the ReceiveText class to query the database and just pull the first text that was actually stored into the database. I have 3 columns, KEY_ROWID, KEY_TITLE, and KEY_BODY. For some reason there is an issue when trying to query the database from the ReceiveText class. Any ideas that would make better sense or be easier to implement? Any help is much appreciated.
oh, right. I wasn't paying attention and for some reason I thought it was returning a string. I see now that it's obviously a cursor..
what you are doing sounds fine but you sure really look at the database on the phone/emulator.
open up adb and type
Code:
sqlite3 /data/data/<name_of_app>/databases/<name_of_db>
then you can do
Code:
select * from <name_of_table>
personally, I find it way easier to read if you change the mode with a ".mode line" command(the dot is important).
that should give you a good idea of why it is not working properly.
How do I open up the adb? I looked online, and it says i can run it through command line, or terminal since I use ubuntu, but I'm not sure exactly how to open it. Apologies for my noobness with android development i'm trying to learn!
if you're running it on a phone/tablet, you'll have to set up the adb drivers
but if you're just using the emulator, you're fine
Code:
cd /<path_to_sdk>/platform-tools/
./adb devices ##this should list the device. if not you have a problem.
./adb shell
and now you can execute commands on the device, like sqlite3
Awesome, I got that to work, and i can see the three "texts" that i've added to the database...looks like this...
1|Sleeping|Sleeping...text you when I wake up.
2|Driving|Driving right now...text you when I'm done.
3|Xbox|Playing xbox...text you later.
So this means that my database works. The numbers 1,2, and 3 are the KEY_ROWID, sleeping, driving, and xbox are the KEY_TITLE, and the third part is the KEY_BODY.
For some reason the ReceiveText class is having trouble pulling this information from the database, I really think it has something to do with when i Query the database, it asks for a columnindex, and i am putting a 0 as the parameter. Any ideas? Thanks again for all your help. It's a great learning process.
yeah, there doesn't seem to be a column with _id == 0
that is strange. when you defined it did you use "integer primary key autoincrement"? This should start at 0, I believe.. maybe you deleted your first entry at some point?
try it with 1 and see if that works.
Man, thanks so much for your help...it works! Here is how I did it from the ReceiveText class.
mDbHelper = new TextsDbAdapter(context);
mDbHelper.open();
getText = mDbHelper.fetchText(1);
message = getText.getString(2).toString();
getText is a Cursor type, and message is a String type.
I really appreciate the help, I understand a lot more now. My next step is to set the text to use without having to specify from the ReceiveText class...any suggestions? I was thinking adding another column in the database that would hold either a 1 or 0, and if it is selected, it updates the database and changes that field to a 1, then the ReceiveText class will query the database to return the field that has the 1...make sense? lol
Just to clear this up a bit for those still a little fuzzy:
Code:
Cursor c = db.query(TABLE_NAME3, new String[] {NAMESHORT, LAPTIME, LAPNUMBER}, null ,null, null, null, orderBy);
The column indexes are what you have in the "new String[]" area whether you have 1 or 50 items. So, NAMESHORT is index 0, LAPTIME is index 1 and LAPNUMPER is index 2. It's NOT the column number of where it is in the table
Just a personal preference of mine, but I code all this in the database methods and return what I need from there. Seeing hard-coded numbers in a program always bothers me. Instead of returning cursors I'll return a StringBuilder or ArrayList or whatever. Just sayn'

Quick variable type question.

I have an EditText object (et_travel) on my screen that's asking for miles traveled. I grab that data like this:
Code:
float travel = Float.parseFloat(et_travel.getText().toString());
if(travel > 40000){
I just discover that if someone puts 40000 in the EditText, everything works fine, but if they put 40,000 (adding a comma to the number), I force close on the float travel = ...statement.
How can I evaluate the number without having a problem from the user adding a comma?
you should parse the string before you run it through Float.parseFloat() so that there are no bad characters in the string. use some of the String methods to find and remove bad characters.
Also that parsefloat method probably throws some sort of exception. You could catch it and let your user know that there value is invalid and to try again
Sent from my Desire HD using XDA App
Set the properties on the edittext so it accepts numbers only.
android:inputtype="numberDecimal|numberSigned"
________________________________
http://ron-droid.blogspot.com

[Ultimate Guide][Part 10 released][12/11/2011]Android App Development - From Scratch

This for all those budding developers of android who are willing to develop for android but are unable to find the resources to do so. This tutorial is made my me and how i understood the whole deal. There will me many parts of this. I will keep updating with new development.
If this thread is useful to u plz hit thanks
Buy me a beer DONATE
PLZ VOTE THE THREAD
PART I
INTRODUCTION
Despite the name, Android will not help you create an unstoppable army of emotionless robot warriors on a relentless quest to cleanse the earth of the scourge of humanity. Instead, Android is an open source software stack that includes the operating system, middleware, and key applications along with a set of API libraries for writing mobile applications that can shape the look, feel, and function of mobile handsets.
As a disruptive addition to a mature field, it’s not hard to see why there has been some confusion about
what exactly Android is. Android is not:
A Java ME implementation Android applications are written using the Java language, but
they are not run within a Java ME virtual machine, and Java-compiled classes and executable
will not run natively in Android.
Part of the Linux Phone Standards Forum (LiPS) or the Open Mobile Alliance
Android runs on an open source Linux kernel, but, while their goals are similar,
Android’s complete software stack approach goes further than the focus of these standardsdefi
ning organizations.
Simply an application layer (like UIQ or S60) While it does include an application layer,
“Android” also describes the entire software stack encompassing the underlying operating system,
API libraries, and the applications themselves
Android is made up of several necessary and dependent parts including the following:
1. A hardware reference design that describes the capabilities required of a mobile device in order to support the software stack
2. A Linux operating system kernel that provides the low-level interface with the hardware, memory management, and process control, all optimized for mobile devices
3. Open source libraries for application development including SQLite, WebKit, OpenGL, and a media manager
4. A run time used to execute and host Android applications, including the Dalvik virtual machine and the core libraries that provide Android specific functionality. The run time is designed to be small and efficient for use on mobile devices.
5. An application framework that agnostically exposes system services to the application layer,including the window manager, content providers, location manager, telephony, and peer-to-peer services
6. A user interface framework used to host and launch applications
7. Preinstalled applications shipped as part of the stack
8. A software development kit used to create applications, including the tools, plug-ins, and documentation.
Android SDK Features
As an application-neutral platform, Android gives you the opportunity to create applications that are as much a part of the phone as anything provided out of the box. The following list highlights some of the most noteworthy Android features:
1. No licensing, distribution, or development fees
2. Wi-Fi hardware access
3. GSM, EDGE, and 3G networks for telephony or data transfer, allowing you to make or receive calls or SMS messages, or to send and retrieve data across mobile networks
4. Comprehensive APIs for location-based services such as GPS
5. Full multimedia hardware control including playback and recording using the camera and microphone
6. APIs for accelerometer and compass hardware
7. IPC message passing
8. Shared data stores
9. An integrated open source WebKit-based browser
10. Full support for applications that integrate Map controls as part of their user interface
11. Peer-to-peer (P2P) support using Google Talk
12. Mobile-optimized hardware-accelerated graphics including a path-based 2D graphics library and support for 3D graphics using OpenGL ES.
13. Media libraries for playing and recording a variety of audio/video or still image formats
14. An application framework that encourages reuse of application components and the replacement of native applications
SQLite Database for Data Storage and Retrieval
Android provides a lightweight relational database for each application using SQLite. Your applications can take advantage of the managed relational database engine to store data securely and efficiently.
By default, each application database is sandboxed — its content is available only to the application that created it — but Content Providers supply a mechanism for the managed sharing of these application databases
Extensive Media Support and 2D/3D Graphics
Bigger screens and brighter, higher-resolution displays have helped make mobiles multimedia devices.
To make the most of the hardware available, Android provides graphics libraries for 2D canvas drawing and 3D graphics with OpenGL.
Android also offers comprehensive libraries for handling still images, video, and audio fi les including the MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, and GIF formats.
The Android Software Development Kit (SDK)
The Android software development kit (SDK) includes everything you need to start developing, testing, and debugging Android applications. Included in the SDK download are:
1. The Android APIs The core of the SDK is the Android API libraries that provide developer access to the Android stack. These are the same libraries used at Google to create native Android applications.
2. Development Tools To turn Android source code into executable Android applications, the SDK includes several development tools that let you compile and debug your applications. You will learn more about the developer tools in Chapter 2.
3. The Android Emulator The Android Emulator is a fully interactive Android device emulator featuring several alternative skins. Using the emulator, you can see how your applications will look and behave on a real Android device. All Android applications run within the Dalvik VM so that the software emulator is an excellent environment — in fact, as it is hardware-neutral, it provides a better independent test environment than any single hardware implementation.
4. Full Documentation The SDK includes extensive code-level reference information detailing exactly what’s included in each package and class and how to use them. In addition to the code documentation, Android’s reference documentation explains how to get started and gives detailed explanations of the fundamentals behind Android development.
5. Sample Code The Android SDK includes a selection of sample applications that demonstrate some of the possibilities available using Android, as well as simple programs that highlight how to use individual API features.
6. Online Support Despite its relative youth, Android has generated a vibrant developer community.
Understanding the Android Software Stack
The Android software stack is composed of the elements shown in Figure 1-1 and described in further detail below it. Put simply, a Linux kernel and a collection of C/C++ libraries are exposed through an application framework that provides services for, and management of, the run time and applications.
1. Linux Kernel Core services (including hardware drivers, process and memory management, security, network, and power management) are handled by a Linux 2.6 kernel. The kernel also provides an abstraction layer between the hardware and the remainder of the stack.
2. Libraries Running on top of the kernel, Android includes various C/C++ core libraries such
as libc and SSL, as well as:
I. A media library for playback of audio and video media
II. A Surface manager to provide display management
III. Graphics libraries that include SGL and OpenGL for 2D and 3D graphics
IV. SQLite for native database support
V. SSL and WebKit for integrated web browser and Internet security
3. Android Run Time What makes an Android phone an Android phone rather than a mobile Linux implementation is the Android run time. Including the core libraries and the Dalvik virtual Machine, the Android run time is the engine that powers your applications and, along with the libraries, forms the basis for the application framework.
4. Core Libraries While Android development is done in Java, Dalvik is not a Java VM. The core Android libraries provide most of the functionality available in the core Java libraries as well as the Android-specific libraries.
I. Dalvik Virtual Machine Dalvik is a register-based virtual machine that’s been optimized to ensure that a device can run multiple instan
[Ultimate Guide][Part 2-1/09/2011]Android App Development - From Scrap
PART II
All you need to start writing your own Android applications is a copy of the Android SDK and the Java development kit. Unless you’re a masochist, you’ll probably want a Java IDE — Eclipse is particularly well supported — to make development a little easier.
Versions of the SDK, Java, and Eclipse are available for Windows, Mac OS, and Linux, so you can explore Android from the comfort of whatever OS you favor. The SDK includes an emulator for all three OS environments, and because Android applications are run on a virtual machine, there’s no advantage to developing from any particular operating system.
Android code is written using Java syntax, and the core Android libraries include most of the features from the core Java APIs. Before they can be run, though, your projects are first translated into Dalvik byte code. As a result, you get the benefits of using Java, while your applications have the advantage of running on a virtual machine optimized for Android devices.
The SDK download includes all the Android libraries, full documentation, and excellent sample applications. It also includes tools to help you write and debug your applications, like the Android Emulator to run your projects and the Dalvik Debug Monitoring Service (DDMS) to help debug them.
Needed
Because Android applications run within the Dalvik virtual machine, you can write them on any platform that supports the developer tools. This currently includes the following:
❑ Microsoft Windows (XP or Vista)
❑ Mac OS X 10.4.8 or later (Intel chips only)
❑ Linux
To get started, you’ll need to download and install the following:
❑ The Android SDK
❑ Java Development Kit (JDK) 5 or 6
You can download the latest JDK from Sun at
http://java.sun.com/javase/downloads/index.jsp
If you already have a JDK installed, make sure that it meets the version requirements listed above, and
note that the Java runtime environment (JRE) is not sufficient
.
Downloading and Installing the SDK
The Android SDK is completely open. There’s no cost to download and use the API, and Google doesn’t charge to allow distribution of your finished programs. You can download the latest version of the SDK for your development platform from the Android development home page at-
http://code.google.com/android/download.html
The SDK is presented as a ZIP fi le containing the API libraries, developer tools, documentation, and several sample applications and API demos that highlight the use of particular API features. Install it by unzipping the SDK into a new folder. (Take note of this location, as you’ll need it later.) The examples and step-by-step instructions provided are targeted at developers using Eclipse with the Android Developer Tool (ADT) plug-in. Neither is required, though — you can use any text editor or Java IDE you’re comfortable with and use the developer tools in the SDK to compile, test, and debug the code snippets and sample applications.
If you’re planning to use them, the next sections explain how to set up Eclipse and the ADT plug-in as your Android development environment. Later in the chapter, we’ll also take a closer look at the developer tools that come with the SDK, so if you’d prefer to develop without using Eclipse or the ADT plugin, you’ll particularly want to check that out.
Developing with Eclipse
Using Eclipse with the ADT plug-in for your Android development offers some significant advantages. Eclipse is an open source IDE (integrated development environment) particularly popular for Java development. It’s available to download for each of the development platforms supported by Android (Windows, Mac OS, and Linux) from the Eclipse foundation homepage:
www.eclipse.org/downloads/
There are many variations available when selecting your Eclipse download; the following is the recommended configuration for Android:
❑ Eclipse 3.3, 3.4 (Ganymede)
❑ Eclipse JDT plug-in
❑ WST
WST and the JDT plug-in are included in most Eclipse IDE packages.
Installing Eclipse consists of uncompressing the download into a new folder. When that’s done, run the Eclipse executable. When it starts for the first time, create a new workspace for your Android development.
Using the Eclipse Plug-in
The ADT plug-in for Eclipse simplifies your Android development by integrating the developer tools, including the emulator and .class-to-.dex converter, directly into the IDE. While you don’t have to use the ADT plug-in, it does make creating, testing, and debugging your applications faster and easier.
The ADT plug-in integrates the following into Eclipse:
❑ An Android Project Wizard that simplifi es creating new projects and includes a basic application template
❑ Forms-based manifest, layout, and resource editors to help create, edit, and validate your XML resources
❑ Automated building of Android projects, conversion to Android executables (.dex), packaging to package fi les (.apk), and installation of packages onto Dalvik virtual machines
❑ The Android Emulator, including control of the emulator’s appearance, network connection settings, and the ability to simulate incoming calls and SMS messages
❑ The Dalvik Debug Monitoring Service (DDMS), which includes port forwarding; stack, heap, and thread viewing; process details; and screen capture facilities
❑ Access to the device or emulator’s fi lesystem, allowing you to navigate the folder tree and transfer files
❑ Runtime debugging, so you can set breakpoints and view call stacks
❑ All Android/Dalvik log and console outputs
Installing the ADT Plug-in
Install the developer tools plug-in by the following steps:
1. Select Help ➪ Software Updates ➪ Find and Install … from within Eclipse.
2. In the resulting dialog box, choose Search for new features to install.
3. Select New Remote Site, and enter the following address into the dialog box, and enter-
https://dl-ssl.google.com/android/eclipse/
4. The new site you entered should now be checked. Click Finish.
5. Eclipse will now download the plug-in. When it’s fi nished, select Android Plugin ➪ Developer Tools from the resulting Search Results dialog box, and click Next.
6. Read and then Accept the terms of the license agreement, and click Next and then Finish. As the ADT plug-in is not signed, you’ll be prompted before the installation continues.
7. When complete, you’ll have to restart Eclipse and update the ADT preferences. Restart and select Window ➪ Preferences … (or Eclipse ➪ Preferences for the Mac OS).
8. Then select Android from the left panel.
9. Click Browse …, and navigate to the folder into which you unzipped the Android SDK, then click Apply and OK.
If u have an installation error of org.eclipse.wst.sse.ui. then insatall software from the following-
Eclipse 3.6 (Helios):http://download.eclipse.org/releases/helios
Eclipse 3.5 (Galileo):http://download.eclipse.org/releases/galileo
Eclipse 3.4 (Ganymede):http://download.eclipse.org/releases/ganymede/
Updating the Plug-in
As the Android SDK matures, there are likely to be frequent updates to the ADT plug-in. In most cases, to update your plug-in, you simply:
1. Navigate to Help ➪ Software Updates ➪ Find and Install …
2. Select Search for updates of the currently installed features, and click Finish …
3. If there are any ADT updates available, they will be presented. Simply select them and choose Install.
Sometimes a plug-in upgrade is so significant that the dynamic update mechanism can’t be used. In those cases, you may have to remove the previous plug-in completely before installing the newer version as described in the previous section.
Starting a new project
To create a new Android project using the Android New Project Wizard:
1. Select File ➪ New ➪ Project.
2. Select the Android Project application type from the Android folder, and click Finish.
3. In the dialog that appears, enter the details for your new project. The “Project name” is the name of your project fi le; the “Package name” specifies its package; the “Activity name” is the name of the class that is your initial Activity; and the “Application name” is the friendly name for your application.
4. When you’ve entered the details, click Finish.
The ADT plug-in then creates a new project that includes a new class that extends Activity. Rather than being completely empty, the default template implements “Hello World.” Before modifying the project, take this opportunity to configure run and debug launch configurations.
Creating a Launch Configuration
Launch configurations let you specify runtime options for running and debugging applications. Using
a launch configuration you can specify the following:
❑ The Project and Activity to launch
❑ The emulator options to use
❑ Input/output settings (including console defaults)
You can specify different launch configurations for Run and Debug modes. The following steps show how to create a launch configuration for an Android application:
1. Select Run ➪ Open Run Dialog … (or Run ➪ Open Debug Dialog …).
2. Right-click Android Application on the project type list, and select New.
3. Enter a name for the configuration. You can create multiple configurations for each project, so create a descriptive title that will help you identify this particular setup.
4. Now choose your start-up options. The first (Android) tab lets you select the project and Activity that you want to start when you run (or debug) the application.
5. Use the Target tab to configure the emulator. There are options to choose the screen size, device skin, and network connection settings. You can also optionally wipe the user data on the emulator and enable or disable the start-up animation. Using the command-line textbox, you can specify additional emulator start-up options if needed.
6. Finally, set any additional properties in the Common tab.
7. Click Apply, and your launch configuration will be saved
Running and Debugging Your Android Applications
You’ve created your first project and created the run and debug configurations for it. Before making any changes, test your installation and configurations by running and debugging the Hello World project. From the Run menu, select Run or Debug to launch the most recently selected configuration, or select Open Run Dialog … or Open Debug Dialog … to select a configuration to use.
If you’re using the ADT plug-in, running or debugging your application:
❑ Compiles the current project and converts it to an Android executable (.dex).
❑ Packages the executable and external resources into an Android package (.apk).
❑ Starts the emulator (if it’s not already running).
❑ Installs your application onto the emulator.
❑ Starts your application.
If you’re debugging, the Eclipse debugger will then be attached, allowing you to set breakpoints and debug your code.
Understanding the app
With that confirmed, let’s take a step back and have a real look at your first Android application. Activity is the base class for the visual, interactive components of your application; it is roughly equivalent to a Form in traditional desktop development. The following snippet shows the skeleton code for an Activity-based class; note that it extends Activity, overriding the onCreate method.
package com.paad.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class HelloWorld extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
}
What’s missing from this template is the layout of the visual interface. In Android, visual components are called Views, which are similar to controls in traditional desktop development.
In the Hello World template created by the wizard, the onCreate method is overridden to call setContentView, which lays out the user interface by inflating a layout resource, as highlighted below:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
}
The resources for an Android project are stored in the res folder of your project hierarchy, which includes drawable, layout, and values subfolders. The ADT plug-in interprets these XML resources to provide design time access to them through the R variable.
The following code snippet shows the UI layout defined in the main.xml fi le created by the Android project template:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
androidrientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Hello World, HelloWorld”
/>
</LinearLayout>
Defining your UI in XML and inflating it is the preferred way of implementing your user interfaces, as it neatly decouples your application logic from your UI design. To get access to your UI elements in code, you add identifier attributes to them in the XML definition. You can then use the findViewById method to return a reference to each named item. The following XML snippet shows an ID attribute added to the TextView widget in the Hello World template:
<TextView
android:id=”@+id/myTextView”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Hello World, HelloWorld”
/>
And the following snippet shows how to get access to it in code:
TextView myTextView = (TextView)findViewById(R.id.myTextView);
Alternatively, if you need to, you can create your layout directly in code as shown below:
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
LinearLayout.LayoutParams lp;
lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
LinearLayout.LayoutParams textViewLP;
textViewLP = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
TextView myTextView = new TextView(this);
myTextView.setText(“Hello World, HelloWorld”);
ll.addView(myTextView, textViewLP);
this.addContentView(ll, lp);
}
All the properties available in code can be set with attributes in the XML layout. As well as allowing easier substitution of layout designs and individual UI elements, keeping the visual design decoupled from the application code helps keep the code more concise.
Types of Android Applications
Most of the applications you create in Android will fall into one of the following categories:
❑ Foreground Activity An application that’s only useful when it’s in the foreground and is effectively suspended when it’s not visible.
❑ Background Service An application with limited interaction that, apart from when being configured, spends most of its lifetime hidden.
❑ Intermittent Activity Expects some interactivity but does most of its work in the background. Often these applications will be set up and then run silently, notifying users when appropriate.
Complex applications are difficult to pigeonhole into a single category and include elements of all three. When creating your application, you need to consider how it’s likely to be used and then design it accordingly. Let’s look more closely at some of the design considerations for each application type described above.
Foreground Activities
When creating foreground applications, you need to consider the Activity life cycle carefully so that the Activity switches seamlessly between the foreground and the background. Applications have no control over their life cycles, and a backgrounded application, with no Services,is a prime candidate for cleanup by Android’s resource management. This means that you need to save the state of the application when the Activity becomes invisible, and present the exact same state when
it returns to the foreground.
Background Services
These applications run silently in the background with very little user input. They often listen for messages or actions caused by the hardware, system, or other applications, rather than rely on user interaction. It’s possible to create completely invisible services, but in practice, it’s better form to provide at least some sort of user control. At a minimum, you should let users confirm that the service is running and let them configure, pause, or terminate it as needed.
Intermittent Activities
Often you’ll want to create an application that reacts to user input but is still useful when it’s not the active foreground Activity. These applications are generally a union of a visible controller Activity with an invisible background Service.
These applications need to be aware of their state when interacting with the user. This might mean updating the Activity UI when it’s visible and sending notifications to keep the user updated when it’s in the background.
Android Development Tools
The Android SDK includes several tools and utilities to help you create, test, and debug your projects. For more detail than is included here, check out the Android documentation at:
http://code.google.com/android/intro/tools.html
As mentioned earlier, the ADT plug-in conveniently incorporates most of these tools into the Eclipse IDE, where you can access them from the DDMS perspective, including:
❑ The Android Emulator An implementation of the Android virtual machine designed to run on your development computer. You can use the emulator to test and debug your android applications.
❑ Dalvik Debug Monitoring Service (DDMS) Use the DDMS perspective to monitor and control the Dalvik virtual machines on which you’re debugging your applications.
❑ Android Asset Packaging Tool (AAPT) Constructs the distributable Android package files (.apk).
❑ Android Debug Bridge (ADB) The ADB is a client-server application that provides a link to a running emulator. It lets you copy files, install compiled application packages (.apk), and run shell commands.
The following additional tools are also available:
❑ SQLite3 A database tool that you can use to access the SQLite database fi les created and used by Android
❑ Traceview Graphical analysis tool for viewing the trace logs from your Android application
❑ MkSDCard Creates an SDCard disk image that can be used by the emulator to simulate an external storage card.
❑ dx Converts Java .class bytecode into Android .dex bytecode.
❑ activityCreator Script that builds Ant build fi les that you can then use to compile your Android applications without the ADT plug-in
The Android Emulator
The emulator is the perfect tool for testing and debugging your applications, particularly if you don’t have a real device (or don’t want to risk it) for experimentation. The emulator is an implementation of the Dalvik virtual machine, making it as valid a platform for running
Android applications as any Android phone. Because it’s decoupled from any particular hardware, it’s an excellent baseline to use for testing your applications. A number of alternative user interfaces are available to represent different hardware configurations, each with different screen sizes, resolutions, orientations, and hardware features to simulate a variety
of mobile device types. Full network connectivity is provided along with the ability to tweak the Internet connection speed and
latency while debugging your applications.
The ADT plug-in integrates the emulator into Eclipse so that it’s launched automatically when you run or debug your projects. If you aren’t using the plug-in or want to use the emulator outside of Eclipse, you can telnet into the emulator and control it from its console.
Dalvik Debug Monitor Service (DDMS)
The emulator lets you see how your application will look, behave, and interact, but to really see what’s happening under the surface, you need the DDMS. The Dalvik Debug Monitoring Service is a powerful debugging tool that lets you interrogate active processes, view the stack and heap, watch and pause active threads, and explore the file system of any active emulator. The DDMS perspective in Eclipse also provides simplified access to screen captures of the emulator and the logs generated by LogCat.
If you’re using the ADT plug-in, the DDMS is fully integrated into Eclipse and is available from the DDMS perspective. If you aren’t using the plug-in or Eclipse, you can run DDMS from the command line, and it will automatically connect to any emulator that’s running.
The Android Debug Bridge (ADB)
The Android debug bridge (ADB) is a client-service application that lets you connect with an Android Emulator or device. It’s made up of three components: a daemon running on the emulator, a service that runs on your development hardware, and client applications (like the DDMS) that communicate with the daemon through the service.
As a communications conduit between your development hardware and the Android device/emulator, the ADB lets you install applications, push and pull files, and run shell commands on the target device. Using the device shell, you can change logging settings, and query or modify SQLite databases available on the device.
The ADT tool automates and simplifies a lot of the usual interaction with the ADB, including application installation and update, log files, and file transfer (through the DDMS perspective). To learn more about what you can do with the ADB, check out the documentation at http://code.google.com/android/reference/adb.html.
[Ultimate Guide][Part 3-1/09/2011]Android App Development - From Scrap
PART III
Creating Applications and Activities
Before you start writing Android applications, it’s important to understand how they’re constructed and have an understanding of the Android application life cycle. In this chapter, you’ll be introduced to the loosely coupled components that make up Android applications (and how they’re bound together using the Android manifest).
Android applications consist of loosely coupled components, bound using a project manifest that describes each component and how they interact. There are six components that provide the building blocks for your applications:
❑ Activities Your application’s presentation layer. Every screen in your application will be an extension of the Activity class. Activities use Views to form graphical user interfaces that display information and respond to user actions. In terms of desktop development, an Activity is equivalent to a Form.
❑ Services The invisible workers of your application. Service components run invisibly, updating your data sources and visible Activities and triggering Notifications. They’re used to perform regular processing that needs to continue even when your application’s Activities aren’t active or visible.
❑ Content Providers A shareable data store. Content Providers are used to manage and share application databases. Content Providers are the preferred way of sharing data across application boundaries. This means that you can configure your own Content Providers to permit access from other applications and use Content Providers exposed by others to access their stored data. Android devices include several native Content Providers that expose useful databases like contact information.
❑ Intents A simple message-passing framework. Using Intents, you can broadcast messages system- wide or to a target Activity or Service, stating your intention to have an action performed. The system will then determine the target(s) that will perform any actions as appropriate.
❑ Broadcast Receivers Intent broadcast consumers. By creating and registering a Broadcast Receiver, your application can listen for broadcast Intents that match specific filter criteria. Broadcast Receivers will automatically start your application to respond to an incoming Intent, making them ideal for event-driven applications.
❑ Notifications A user notification framework. Notifications let you signal users without stealing focus or interrupting their current Activities. They’re the preferred technique for getting a user’s attention from within a Service or Broadcast Receiver. For example, when a device receives a text message or an incoming call, it alerts you by flashing lights, making sounds, displaying
icons, or showing dialog messages. You can trigger these same events from your own applications using Notifications
Introducing the Application Manifest
Each Android project includes a manifest file, AndroidManifest.xml, stored in the root of the project hierarchy. The manifest lets you define the structure and metadata of your application and its components. It includes nodes for each of the components (Activities, Services, Content Providers, and Broadcast Receivers) that make up your application and, using Intent Filters and Permissions, determines how they interact with each other and other applications.
It also offers attributes to specify application metadata (like its icon or theme), and additional top-level nodes can be used for security settings. The manifest is made up of a root manifest tag with a package attribute set to the project’s package.
It usually includes an xmlns:android attribute that supplies several system attributes used within the
file.
A typical manifest node is shown in the XML snippet below:
<manifest xmlns:android=http://schemas.android.com/apk/res/android
package=”com.my name.myapp”>
[ ... manifest nodes ... ]
</manifest>
The manifest tag includes nodes that define the application components, security settings, and test classes that make up your application. The following list gives a summary of the available manifest node tags, and an XML snippet demonstrating how each one is used:
❑ application A manifest can contain only one application node. It uses attributes to specify the metadata for your application (including its title, icon, and theme). It also acts as a container that includes the Activity, Service, Content Provider, and Broadcast Receiver tags used to specify the application components.
<application android:icon=”@drawable/icon”
android:theme=”@style/my_theme”>
[ ... application nodes ... ]
</application>
❑ activity An activity tag is required for every Activity displayed by your application, using the android:name attribute to specify the class name. This must include the main launch Activity and any other screen or dialogs that can be displayed. Trying to start an Activity that’s not defined in the manifest will throw a runtime exception. Each Activity node supports intent-filter child tags that specify which Intents launch the Activity.
<activity android:name=”.MyActivity” android:label=”@string/name”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
❑ service As with the activity tag, create a new service tag for each Service class used in your application. Service tags also
support intent-filter child tags to allow late runtime binding.
<service android:enabled=”true” android:name=”.MyService”></service>
❑ provider Provider tags are used for each of your application’s Content Providers. Content Providers are used to manage database access and sharing within and between applications.
<provider androidermission=”com.paad.MY_PERMISSION”
android:name=”.MyContentProvider”
android:enabled=”true”
android:authorities=”com.paad.myapp.MyContentProvider”>
</provider>
❑ receiver By adding a receiver tag, you can register a Broadcast Receiver without having to launch your application fi rst. As Broadcast Receivers are like global event listeners that, once registered, will execute whenever a matching Intent is broadcast by an application. By registering a Broadcast Receiver in the manifest, you can make this process entirely autonomous. If a matching Intent is broadcast, your application will be started automatically and the registered Broadcast Receiver
will be run.
<receiver android:enabled=”true”
android:label=”My Broadcast Receiver”
android:name=”.MyBroadcastReceiver”>
</receiver>
❑ uses-permission As part of the security model, uses-permission tags declare the permissions you’ve determined that your application needs for it to operate properly. The permissions you include will be presented to the user, to grant or deny, during installation. Permissions are required for many of the native Android services, particularly those with a cost or security implication (such as dialing, receiving SMS, or using the location-based services). As shown in the item below, third-party applications, including your own, can also specify permissions before providing access to shared application components.
<uses-permission android:name=”android.permission.ACCESS_LOCATION”>
</uses-permission>
❑ permission Before you can restrict access to an application component, you need to define a permission in the manifest. Use the permission tag to create these permission definitions. Application components can then require them by adding the androidermission attribute. Other applications will then need to include a uses-permission tag in their manifests (and
have it granted) before they can use these protected components. Within the permission tag, you can specify the level of access the permission will permit (normal, dangerous, signature, signatureOrSystem), a label, and an external resource containing the description that explain the risks of granting this permission.
<permission android:name=”com.paad.ALERT_DEVICE”
androidrotectionLevel=”dangerous”
android:label=”Alert”
android:description=”@string/detonate_description”>
</permission>
❑ instrumentation Instrumentation classes provide a framework for running tests on your Activities and Services at run time. They provide hooks to monitor your application and its interaction with the system resources. Create a new node for each of the test classes you’ve created for your application.
<instrumentation android:label=”My Test”
android:name=”.MyTestClass”
android:targetPackage=”com.paad.aPackage”>
</instrumentation>
The ADT New Project Wizard automatically creates a new manifest file when it creates a new project. You’ll return to the manifest as each of the application components is introduced.
Using the Manifest Editor
The ADT plug-in includes a visual Manifest Editor to manage your manifest, rather than you having to manipulate the underlying XML directly. To use the Manifest Editor in Eclipse, right-click the AndroidManifest.xml fi le in your project folder, and select Open With ➪ Android Manifest Editor. This presents the Android Manifest Overview screen. This gives you a high-level view of your application structure and provides shortcut links to the Application, Permissions, Instrumentation, and raw XML screens. Each of the next three tabs contains a visual interface for managing the application, security, and instrumentation (testing) settings, while the last tag (using the manifest’s filename) gives access to the raw XML.
Application Life Cycle
Android applications have no control over their own life cycles. Instead, application components must listen for changes in the application state and react accordingly, taking particular care to be prepared for untimely termination. As mentioned before, by default, each Android application is run in its own process that’s running a separate instance of Dalvik. Memory and process management of each application is handled exclusively by the run time. Android aggressively manages its resources, doing whatever it takes to ensure that the device remains responsive. This means that processes will be killed, without warning if necessary, to free resources for higher-priority application, generally those that are interacting directly with the user at the time.
Understanding Application Priority and Process States
The order in which processes are killed to reclaim resources is determined by the priority of the hosted applications. An application’s priority is equal to its highest-priority component.
The following list details each of the application states, explaining how the state is determined by the application components comprising it:
❑ Active Processes Active (foreground) processes are those hosting applications with components currently interacting with the user. These are the processes Android is trying to keep responsive by reclaiming resources. There are generally very few of these processes, and they will be killed only as a last resort.
❑ Visible Processes Visible, but inactive processes are those hosting “visible” Activities. As the name suggests, visible Activities are visible, but they aren’t in the foreground or responding to user events. This happens when an Activity is only partially obscured (by a non-full-screen or transparent Activity). There are generally very few visible processes, and they’ll only be killed in extreme circumstances to allow active processes to continue.
❑ Started Service Processes Processes hosting Services that have been started. Services support ongoing processing that should continue without a visible interface. Because Services don’t interact directly with the user, they receive a slightly lower priority than visible Activities. They are still considered to be foreground processes and won’t be killed unless resources are needed
for active or visible processes.
❑ Background Processes Processes hosting Activities that aren’t visible and that don’t have any Services that have been started are considered background processes. There will generally be a large number of background processes that Android will kill using a last-seen-first-killed pattern to obtain resources for foreground processes.
❑ Empty Processes To improve overall system performance, Android often retains applications in memory after they have reached the end of their lifetimes. Android maintains this cache to improve the start-up time of applications when they’re re-launched. These processes are routinely killed as required.
Creating Resources
Application resources are stored under the res/ folder of your project hierarchy. In this folder, each of the available resource types can have a subfolder containing its resources. If you start a project using the ADT wizard, it will create a res folder that contains subfolders for the values, drawable, and layout resources that contain the default layout, application icon, and string
resource definitions, respectively. There are seven primary resource types that have different folders: simple values, drawables, layouts, animations, XML, styles, and raw resources. When your application is built, these resources will be
compiled as efficiently as possible and included in your application package.
This process also creates an R class file that contains references to each of the resources you include in your project. This lets you reference the resources in your code, with the advantage of design time syntax checking.
Creating Simple Values
Supported simple values include strings, colors, dimensions, and string or integer arrays. All simple values are stored within XML fi les in the res/values folder. Within each XML fi le, you indicate the type of value being stored using tags as shown in the sample XML file below:
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string name=”app_name”>To Do List</string>
<color name=”app_background”>#FF0000FF</color>
<dimen name=”default_border”>5px</dimen>
<array name=”string_array”>
<item>Item 1</item>
<item>Item 2</item>
<item>Item 3</item>
</array>
<array name=”integer_array”>
<item>3</item>
<item>2</item>
<item>1</item>
</array>
</resources>
Strings
Externalizing your strings helps maintain consistency within your application and makes it much easier to create localized versions. String resources are specified using the string tag as shown in the following XML snippet:
<string name=”stop_message”>Stop.</string>
Android supports simple text styling, so you can use the HTML tags <b>, <i>, and <u> to apply bold, italics, or underlining to parts of your text strings as shown in the example below:
<string name=”stop_message”><b>Stop.</b></string>
You can use resource strings as input parameters for the String.format method. However, String.format does not support the text styling described above. To apply styling to a format string, you have to escape the HTML tags when creating your resource, as shown below:
<string name=”stop_message”><b>Stop</b>. %1$s</string>
Within your code, use the Html.fromHtml method to convert this back into a styled character sequence:
String rString = getString(R.string.stop_message);
String fString = String.format(rString, “Collaborate and listen.”);
CharSequence styledString = Html.fromHtml(fString);
Colors
Use the color tag to define a new color resource. Specify the color value using a # symbol followed by the (optional) alpha channel, then the red, green, and blue values using one or two hexadecimal numbers with any of the following notations:
❑ #RGB
❑ #RRGGBB
❑ #ARGB
❑ #ARRGGBB
Dimensions
Dimensions are most commonly referenced within style and layout resources. They’re useful for creating layout constants such as borders and font heights. To specify a dimension resource, use the dimen tag, specifying the dimension value, followed by an
identifi er describing the scale of your dimension:
❑ px Screen pixels
❑ in Physical inches
❑ pt Physical points
❑ mm Physical millimeters
❑ dp Density-independent pixels relative to a 160-dpi screen
❑ sp Scale-independent pixels
Styles and Themes
Style resources let your applications maintain a consistent look and feel by specifying the attribute values used by Views. The most common use of themes and styles is to store the colors and fonts for an application. To create a style, use a style tag that includes a name attribute, and contains one or more item tags.
Each item tag should include a name attribute used to specify the attribute (such as font size or color) being defened. The tag itself should then contain the value, as shown in the raw code below:
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<style name=”StyleName”>
<item name=”attributeName”>value</item>
</style>
</resources>
Drawables
Drawable resources include bitmaps and NinePatch (stretchable PNG) images. They are stored as individual files in the res/drawable folder. The resource identifier for a bitmap resource is the lowercase filename without an extension.
The preferred format for a bitmap resource is PNG, although JPG and GIF files are also supported.
NinePatch (or stretchable) images are PNG fi les that mark the parts of an image that can be stretched. NinePatch images must be properly defined PNG files that end in .9.png. The resource identifier for NinePatches is the filename without the trailing .9.png.
A NinePatch is a variation of a PNG image that uses a 1-pixel border to define the area of the image that can be stretched if the image is enlarged. To create a NinePatch, draw single-pixel black lines that represent stretchable areas along the left and top borders of your image. The unmarked sections won’t be resized, and the relative size of each of the marked sections will remain the same as the image size changes.
Layouts
Layout resources let you decouple your presentation layer by designing user-interface layouts in XML rather than constructing them in code. The most common use of a layout is to defi ne the user interface for an Activity. Once defined in XML, the layout is “inflated” within an Activity using setContentView, usually within the onCreate method.
Animations
Android supports two types of animation. Tweened animations can be used to rotate, move, stretch, and fade a View; or you can create frame-by-frame animations to display a sequence of drawable images. Defining animations as external resources allows you to reuse the same sequence in multiple places and provides you with the opportunity to present an alternative animation based on device hardware or orientation.
An animation can be defined for changes in alpha (fading), scale (scaling), translate (moving), or rotate (rotating).
Each of these animation types supports attributes to define what the sequence will do:
Alpha fromAlpha and toAlpha Float from 0 to 1
Scale fromXScale/toXScale Float from 0 to 1
fromYScale/toYScale Float from 0 to 1
pivotX/pivotY String of the percentage of graphic width/height from 0% to 100%
Translate fromX/toX Float from 0 to 1
fromY/toY Float from 0 to 1
Rotate fromDegrees/toDegrees Float from 0 to 360
pivotX/pivotY String of the percentage of graphic width/height from 0% to 100%
You can create a combination of animations using the set tag. An animation set contains one or more animation transformations and supports various additional tags and attributes to customize when and how each animation within the set is run.
The following list shows some of the set tags available:
❑ duration Duration of the animation in milliseconds.
❑ startOffset Millisecond delay before starting this animation.
❑ fi llBefore True to apply the animation transformation before it begins.
❑ fi llAfter True to apply the animation transformation after it begins.
❑ interpolator Interpolator to set how the speed of this effect varies over time.
[Ultimate Guide][Part 4-1/09/2011]Android App Development - From Scrap
PART IV
Resources
As well as the resources you create, Android supplies several system resources that you can use in your applications. The resources can be used directly from your application code and can also be referenced from within other resources.
Using Resources in Code
You access resources in code using the static R class. R is a generated class based on your external resources and created by compiling your project. The R class contains static subclasses for each of the resource types for which you’ve defi ned at least one resource. For example, the default new project includes the R.string and R.drawable subclasses.
If you are using the ADT plug-in in Eclipse, the R class will be created automatically when you make any change to an external resource fi le or folder. If you are not using the plug-in, use the AAPT tool to compile your project and generate the R class. R is a compiler-generated class, so don’t make any manual modifi cations to it as they will be lost when the fi le is regenerated.
Each of the subclasses within R exposes its associated resources as variables, with the variable names matching the resource identifi ers — for example, R.string.app_name or R.drawable.icon. The value of these variables is a reference to the corresponding resource’s location in the resource table, not an instance of the resource itself.
Where a constructor or method, such as setContentView, accepts a resource identifi er, you can pass in the resource variable, as shown in the code snippet below:
// Inflate a layout resource.
setContentView(R.layout.main);
// Display a transient dialog box that displays the
// error message string resource.
Toast.makeText(this, R.string.app_error, Toast.LENGTH_LONG).show();
When you need an instance of the resource itself, you’ll need to use helper methods to extract them from the resource table, represented by an instance of the Resources class. Because these methods perform lookups on the application’s resource table, these helper methods can’t be static. Use the getResources method on your application context as shown in the snippet below to
access your application’s Resource instance:
Resources myResources = getResources();
The Resources class includes getters for each of the available resource types and generally works by passing in the resource ID you’d like an instance of. The following code snippet shows an example of using the helper methods to return a selection of resource values:
Resources myResources = getResources();
CharSequence styledText = myResources.getText(R.string.stop_message);
Drawable icon = myResources.getDrawable(R.drawable.app_icon);
int opaqueBlue = myResources.getColor(R.color.opaque_blue);
float borderWidth = myResources.getDimension(R.dimen.standard_border);
Animation tranOut;
tranOut = AnimationUtils.loadAnimation(this, R.anim.spin_shrink_fade);
String[] stringArray;
stringArray = myResources.getStringArray(R.array.string_array);
int[] intArray = myResources.getIntArray(R.array.integer_array);
Frame-by-frame animated resources are inflated into AnimationResources. You can return the value using getDrawable and casting the return value as shown below:
AnimationDrawable rocket;
rocket = (AnimationDrawable)myResources.getDrawable(R.drawable.frame_by_frame);
Referencing Resources in Resources
You can also reference resources to use as attribute values in other XML resources. This is particularly useful for layouts and styles, letting you create specialized variations on themes and localized strings and graphics. It’s also a useful way to support different images and spacing for a layout to ensure that it’s optimized for different screen sizes and resolutions.
To reference one resource from another, use @ notation, as shown in the following snippet:
attribute=”@[packagename:]resourcetype/resourceidentifier”
The following snippet creates a layout that uses color, dimension, and string resources:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
androidrientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
androidadding=”@dimen/standard_border”>
<EditText
android:id=”@+id/myEditText”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/stop_message”
android:textColor=”@color/opaque_blue”
/>
</LinearLayout>
Using System Resources
The native Android applications externalize many of their resources, providing you with various strings, images, animations, styles, and layouts to use in your applications. Accessing the system resources in code is similar to using your own resources. The difference is that you use the native android resource classes available from android.R, rather than the application-specifi c R
class. The following code snippet uses the getString method available in the application context to retrieve an error message available from the system resources:
CharSequence httpError = getString(android.R.string.httpErrorBadUrl);
To access system resources in XML, specify Android as the package name, as shown in this XML snippet:
<EditText
android:id=”@+id/myEditText”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@android:string/httpErrorBadUrl”
android:textColor=”@android:color/darker_gray”
/>
Referring to Styles in the Current Theme
Themes are an excellent way to ensure consistency for your application’s UI. Rather than fully define each style, Android provides a shortcut to let you use styles from the currently applied theme.
To do this, you use ?android: rather than @ as a prefix to the resource you want to use. The following example shows a snippet of the above code but uses the current theme’s text color rather than an external resource:
<EditText
android:id=”@+id/myEditText”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/stop_message”
android:textColor=”?android:textColor”
/>
This technique lets you create styles that will change if the current theme changes, without having to
modify each individual style resource.
Creating Resources for Different Languages and Hardware
One of the most powerful reasons to externalize your resources is Android’s dynamic resource selection mechanism.
Using the structure described below, you can create different resource values for specifi c languages, locations, and hardware confi gurations that Android will choose between dynamically at run time. This lets you create language-, location-, and hardware-specifi c user interfaces without having to change your code.
Specifying alternative resource values is done using a parallel directory structure within the res/ folder, using hyphen (-) separated text qualifi ers to specify the conditions you’re supporting. The example hierarchy below shows a folder structure that features default string values, along with a French language alternative with an additional Canadian location variation:
Project/
res/
values/
strings.xml
values-fr/
strings.xml
values-fr-rCA/
strings.xml
The following list gives the available qualifiers you can use to customize your resource files:
❑ Language Using the lowercase two-letter ISO 639-1 language code.
❑ Region A lowercase “r” followed by the uppercase two-letter ISO 3166-1-alpha-2 language code.
❑ Screen Orientation One of port (portrait), land (landscape), or square (square)
❑ Screen Pixel Density Pixel density in dots per inch (dpi)
❑ Touchscreen Type One of notouch, stylus, or finger
❑ Keyboard Availability Either of keysexposed or keyshidden
❑ Keyboard Input Type One of nokeys, qwerty, or 12key
❑ UI Navigation Type One of notouch, dpad, trackball, or wheel
❑ Screen Resolution Screen resolution in pixels with the largest dimension first
You can specify multiple qualifiers for any resource type, separating each qualifier with a hyphen. Any combination is supported; however, they must be used in the order given in the list above, and no more than one value can be used per qualifier.
The following example shows valid and invalid directory names for alternative drawable resources.
❑ Valid:
drawable-en-rUS
drawable-en-keyshidden
drawable-land-notouch-nokeys-320x240
❑ Invalid:
drawable-rUS-en (out of order)
drawable-rUS-rUK (multiple values for a single qualifier)
Runtime Configuration Changes
Android supports runtime changes to the language, location, and hardware by terminating and restarting each application and reloading the resource values. This default behavior isn’t always convenient or desirable, particularly as some configuration changes (like screen orientation and keyboard visibility) can occur as easily as a user rotating the device or sliding out the keyboard. You can customize your application’s response to these changes by detecting and reacting to them yourself.
To have an Activity listen for runtime configuration changes, add an android:configChanges attribute to its manifest node, specifying the configuration changes you want to handle.
The following list describes the configuration changes you can specify:
❑ orientation The screen has been rotated between portrait and landscape.
❑ keyboardHidden The keyboard has been exposed or hidden.
❑ fontScale The user has changed the preferred font size.
❑ locale The user has chosen a different language setting.
❑ keyboard The type of keyboard has changed; for example, the phone may have a 12 keypad
that flips out to reveal a full keyboard.
❑ touchscreen or navigation The type of keyboard or navigation method has changed. Neither
of these events should normally happen.
You can select multiple events to handle by separating the values with a pipe (|).
The following XML snippet shows an activity node declaring that it will handle changes in screen orientation
and keyboard visibility:
<activity android:name=”.List”
android:label=”@string/app_name”
android:theme=”@style/ListTheme”
android:configChanges=”orientation|keyboard”/>
Adding this attribute suppresses the restart for the specified configuration changes, instead, triggering the onConfigurationChanged method in the Activity. Override this method to handle the configuration changes using the passed-in Configuration object to determine the new configuration values, as shown in the following skeleton code. Be sure to call back to the super class and reload any resource values that the Activity uses in case they’ve changed.
@Override
public void onConfigurationChanged(Configuration _newConfig) {
super.onConfigurationChanged(_newConfig);
[ ... Update any UI based on resource values ... ]
if (_newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
[ ... React to different orientation ... ]
}
if (_newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {
[ ... React to changed keyboard visibility ... ]
}
}
When onConfigurationChanged is called, the Activity’s Resource variables will have already been updated with the new values so they’ll be safe to use. Any configuration change that you don’t explicitly flag as being handled by your application will still cause an application restart without a call to onConfigurationChanged.
Activity
To create a new Activity, you extend the Activity class, defining the user interface and implementing your functionality. The basic skeleton code for a new Activity is shown below:
package com.paad.myapplication;
import android.app.Activity;
import android.os.Bundle;
public class MyActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
}
The base Activity class presents an empty screen that encapsulates the window display handling functionality. An empty Activity isn’t particularly useful, so the first thing you’ll want to do is lay out the screen interface using Views and layouts. Activity UIs are created using Views. Views are the user-interface controls that display data and provide user interaction. Android provides several layout classes, called View Groups, that can contain multiple Views to help you design compelling user interfaces.
To assign a user interface to an Activity, call setContentView from the onCreate method of your Activity.
In this first snippet, a simple instance of MyView is used as the Activity’s user interface:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MyView myView = new MyView(this);
setContentView(myView);
}
More commonly you’ll want to use a more complex UI design. You can create a layout in code using layout View Groups, or you can use the standard Android convention of passing a resource ID for a layout defined in an external resource, as shown in the snippet below:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
}
In order to use an Activity in your application, you need to register it in the manifest. Add new activity tags within the application node of the manifest; the activity tag includes attributes for metadata such as the label, icon, required permissions, and themes used by the Activity. An Activity without a corresponding activity tag can’t be started. The following XML snippet shows how to add a node for the MyActivity class created in the snippets above:
<activity android:label=”@string/app_name”
android:name=”.MyActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
Within the activity tag, you can add intent-filter nodes that specify the Intents your Activity will listen for and react to. Each Intent Filter defi nes one or more actions and categories that your Activity supports. make an Activity available from the main program launcher, it must include an Intent Filter listening for the Main action and the Launcher category, as highlighted in the snippet below:
<activity android:label=”@string/app_name”
android:name=”.MyActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
Activity States
As activities are created and destroyed, they move in and out of the stack. As they do so, they transition through four possible states:
❑ Active When an Activity is at the top of the stack, it is the visible, focused, foreground activity that is receiving user input. Android will attempt to keep it alive at all costs, killing Activities further down the stack as needed, to ensure that it has the resources it needs. When another Activity becomes active, this one will be paused.
❑ Paused In some cases, your Activity will be visible but will not have focus; at this point, it’s paused. This state is reached if a transparent or non-full-screen Activity is active in front of it. When paused, an Activity is treated as if it were active; however, it doesn’t receive user input events. In extreme cases, Android will kill a paused Activity to recover resources for the active
Activity. When an Activity becomes totally obscured, it becomes stopped.
❑ Stopped When an Activity isn’t visible, it “stops.” The Activity will remain in memory retaining all state and member information; however, it is now a prime candidate for execution when the system requires memory elsewhere. When an Activity is stopped, it’s important to save data and the current UI state. Once an Activity has exited or closed, it becomes inactive.
❑ Inactive After an Activity has been killed, and before it’s been launched, it’s inactive. Inactive Activities have been removed from the Activity stack and need to be restarted before they can be displayed and used.
State transitions are nondeterministic and are handled entirely by the Android memory manager. Android will start by closing applications that contain inactive Activities, followed by those that are stopped, and in extreme cases, it will remove those that are paused. To ensure a seamless user experience, transitions between these states should be invisible to the user. There should be no difference between an Activity moving from paused, stopped, or killed states back to active, so it’s important to save all UI state changes and persist all data when an Activity is paused or stopped. Once an Activity does become active, it should restore those saved values
[Ultimate Guide][Part 5-2/09/2011]Android App Development - From Scrap
PART V
Monitoring State Changes
To ensure that Activities can react to state changes, Android provides a series of event handlers that are fired when an Activity transitions through its full, visible, and active lifetimes. The following skeleton code shows the stubs for the state change method handlers available in an Activity.
package com.paad.myapplication;
import android.app.Activity;
import android.os.Bundle;
public class MyActivity extends Activity {
// Called at the start of the full lifetime.
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Initialize activity.
}
// Called after onCreate has finished, use to restore UI state
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
}
// Called before subsequent visible lifetimes
// for an activity process.
@Override
public void onRestart(){
super.onRestart();
// Load changes knowing that the activity has already
// been visible within this process.
}
// Called at the start of the visible lifetime.
@Override
public void onStart(){
super.onStart();
// Apply any required UI change now that the Activity is visible.
}
// Called at the start of the active lifetime.
@Override
public void onResume(){
super.onResume();
// Resume any paused UI updates, threads, or processes required
// by the activity but suspended when it was inactive.
}
// Called to save UI state changes at the
// end of the active lifecycle.
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
super.onSaveInstanceState(savedInstanceState);
}
// Called at the end of the active lifetime.
@Override
public void onPause(){
// Suspend UI updates, threads, or CPU intensive processes
// that don’t need to be updated when the Activity isn’t
// the active foreground activity.
super.onPause();
}
// Called at the end of the visible lifetime.
@Override
public void onStop(){
// Suspend remaining UI updates, threads, or processing
// that aren’t required when the Activity isn’t visible.
// Persist all edits or state changes
// as after this call the process is likely to be killed.
super.onStop();
}
// Called at the end of the full lifetime.
@Override
public void onDestroy(){
// Clean up any resources including ending threads,
// closing database connections etc.
super.onDestroy();
}
}
Understanding Activity Lifetimes
Within an Activity’s full lifetime, between creation and destruction, it will go through one or more iterations of the active and visible lifetimes. Each transition will trigger the method handlers described previously.
The Full Lifetime
The full lifetime of your Activity occurs between the first call to onCreate and the final call to onDestroy. It’s possible, in some cases, for an Activity’s process to be terminated without the onDestroy method being called. Use the onCreate method to initialize your Activity: Inflate the user interface, allocate references to class variables, bind data to controls, and create Services and threads. The onCreate method is passed a Bundle object containing the UI state saved in the last call to onSaveInstanceState. You should use this Bundle to restore the user interface to its previous state, either in the onCreate method or by overriding onRestoreInstanceStateMethod. Override onDestroy to clean up any resources created in onCreate, and ensure that all external connections, such as network or database links, are closed. As part of Android’s guidelines for writing efficient code, it’s recommended that you avoid the creation of short-term objects. Rapid creation and destruction of objects forces additional garbage collection, a process that can have a direct impact on the user experience. If your Activity creates the same set of objects regularly, consider creating them in the onCreate method instead, as it’s called only once in the Activity’s lifetime.
The Visible Lifetime
An Activity’s visible lifetimes are bound between calls to onStart and onStop. Between these calls, your Activity will be visible to the user, although it may not have focus and might be partially obscured. Activities are likely to go through several visible lifetimes during their full lifetime, as they move between the foreground and background. While unusual, in extreme cases, the Android run time will kill an Activity during its visible lifetime without a call to onStop. The onStop method should be used to pause or stop animations, threads, timers, Services, or other processes that are used exclusively to update the user interface. There’s little value in consuming resources (such as CPU cycles or network bandwidth) to update the UI when it isn’t visible. Use the onStart (or onRestart) methods to resume or restart these processes when the UI is visible again. The onRestart method is called immediately prior to all but the fi rst call to onStart. Use it to implement special processing that you want done only when the Activity restarts within its full lifetime. The onStart/onStop methods are also used to register and unregister Broadcast Receivers that are being used exclusively to update the user interface. It will not always be necessary to unregister Receivers when the Activity becomes invisible, particularly if they are used to support actions other than updating the UI.
The Active Lifetime
The active lifetime starts with a call to onResume and ends with a corresponding call to onPause. An active Activity is in the foreground and is receiving user input events. Your Activity is likely to go through several active lifetimes before it’s destroyed, as the active lifetime will end when a new Activity is displayed, the device goes to sleep, or the Activity loses focus. Try to keep code in the onPause and onResume methods relatively fast and lightweight to ensure that your application remains responsive when moving in and out of the foreground. Immediately before onPause, a call is made to onSaveInstanceState. This method provides an opportunity to save the Activity’s UI state in a Bundle that will be passed to the onCreate and onRestoreInstanceState methods. Use onSaveInstanceState to save the UI state (such as check button states, user focus, and entered but uncommitted user input) to ensure that the Activity can present the same UI when it next becomes active. During the active lifetime, you can safely assume that onSaveInstanceState and onPause will be called before the process is terminated.
Most Activity implementations will override at least the onPause method to commit unsaved changes, as it marks the point beyond which an Activity may be killed without warning. Depending on your application architecture, you may also choose to suspend threads, processes, or Broadcast Receivers while your Activity is not in the foreground. The onResume method can be very lightweight. You will not need to reload the UI state here as this is handled by the onCreate and onRestoreInstanceState methods when required. Use onResume to re-register any Broadcast Receivers or other processes you may have stopped in onPause.
Creating User Interfaces
It’s vital to create compelling and intuitive User Interfaces for your applications. Ensuring that they are as stylish and easy to use as they are functional should be a primary design consideration.
Basic Android UI Design
Android introduces some new terminology for familiar programming metaphors.
❑ Views Views are the basic User Interface class for visual interface elements (commonly known as controls or widgets). All User Interface controls, and the layout classes, are derived from Views.
❑ ViewGroups View Groups are extensions of the View class that can contain multiple child Views. By extending the ViewGroup class, you can create compound controls that are made up of interconnected child Views. The ViewGroup class is also extended to provide the layout managers, such as LinearLayout, that help you compose User Interfaces.
❑ Activities Activities, represent the window or screen being displayed to the user. Activities are the Android equivalent of a Form. To display a User Interface, you assign a View or layout to an Activity. Android provides several common UI controls, widgets, and layout managers.
For most graphical applications, it’s likely that you’ll need to extend and modify these standard controls, or create composite or entirely new controls to provide your own functionality.
Views
All visual components in Android descend from the View class and are referred to generically as Views. The ViewGroup class is an extension of View designed to contain multiple Views. Generally, View Groups are either used to construct atomic reusable components (widgets) or to manage the layout of child Views. View Groups that perform the latter function are generally referred to as layouts.
Creating Activity User Interfaces with Views
A new Activity starts with a temptingly empty screen onto which you place your User Interface. To set the User Interface, call setContentView, passing in the View instance (typically a layout) to display. Because empty screens aren’t particularly inspiring, you will almost always use setContentView to assign an Activity’s User Interface when overriding its onCreate handler.
The setContentView method accepts either a layout resource ID or a single View instance. This lets you defi ne your User Interface either in code or using the preferred technique of external layout resources. Using layout resources decouples your presentation layer from the application logic, providing the flexibility to change the presentation without changing code. This makes it possible to specify different layouts optimized for different hardware configurations, even changing them at run time based on hardware changes (such as screen orientation).
The following code snippet shows how to set the User Interface for an Activity using an external layout resource. You can get references to the Views used within a layout with the findViewById method. This example assumes that main.xml exists in the project’s res/layout folder.
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView myTextView = (TextView)findViewById(R.id.myTextView);
}
If you prefer the more traditional approach, you can specify the User Interface in code. The following snippet shows how to assign a new TextView as the User Interface:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
TextView myTextView = new TextView(this);
setContentView(myTextView);
myTextView.setText(“Hello, Android”);
}
The setContentView method accepts a single View instance; as a result, you have to group multiple controls to ensure that you can reference a layout using a single View or View Group.
The Android Widget Toolbox
Android supplies a toolbox of standard Views to help you create simple interfaces. By using these controls (and modifying or extending them as necessary), you can simplify your development and provide consistency between applications. Android also supports several more advanced View implementations including date-time pickers, auto-complete input boxes, maps, galleries, and tab sheets. For a more comprehensive list of the available widgets, follow this link
http://code.google.com/android/reference/view-gallery.html.
Layouts
Layout Managers (more generally, “layouts”) are extensions of the ViewGroup class designed to control the position of child controls on a screen. Layouts can be nested, letting you create arbitrarily complex interfaces using a combination of Layout Managers. The Android SDK includes some simple layouts to help you construct your UI. It’s up to you to select the right combination of layouts to make your interface easy to understand and use. The following list includes some of the more versatile layout classes available:
❑ FrameLayout The simplest of the Layout Managers, the Frame Layout simply pins each child view to the top left corner. Adding multiple children stacks each new child on top of the previous, with each new View obscuring the last.
❑ LinearLayout A Linear Layout adds each child View in a straight line, either vertically or horizontally. A vertical layout has one child View per row, while a horizontal layout has a single row of Views. The Linear Layout Manager allows you to specify a “weight” for each child View that controls the relative size of each within the available space.
❑ RelativeLayout Using the Relative Layout, you can define the positions of each of the child Views relative to each other and the screen boundaries.
❑ TableLayout The Table Layout lets you lay out Views using a grid of rows and columns. Tables can span multiple rows and columns, and columns can be set to shrink or grow.
❑ AbsoluteLayout In an Absolute Layout, each child View’s position is defined in absolute coordinates. Using this class, you can guarantee the exact layout of your components, but at a price. Compared to the previous managers, describing a layout in absolute terms means that your layout can’t dynamically adjust for different screen resolutions and orientations.
The Android documentation describes the features and properties of each layout class in detail.
Link: http://code.google.com/android/devel/ui/layout.html.
Using Layouts
The preferred way to implement layouts is in XML using external resources. A layout XML must contain a single root element. This root node can contain as many nested layouts and Views as necessary to construct an arbitrarily complex screen.
The following XML snippet shows a simple layout that places a TextView above an EditText control using a LinearLayout configured to lay out vertically:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
androidrientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Enter Text Below”
/>
<EditText
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Text Goes Here!”
/>
</LinearLayout>
Implementing layouts in XML decouples the presentation layer from View and Activity code. It also lets you create hardware-specific variations that are dynamically loaded without requiring code changes. When it’s preferred, or required, you can implement layouts in code. When assigning Views to layouts, it’s important to apply LayoutParameters using the setLayoutParams method, or passing them in to the addView call as shown below:
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
TextView myTextView = new TextView(this);
EditText myEditText = new EditText(this);
myTextView.setText(“Enter Text Below”);
myEditText.setText(“Text Goes Here!”);
int lHeight = LinearLayout.LayoutParams.FILL_PARENT;
int lWidth = LinearLayout.LayoutParams.WRAP_CONTENT;
ll.addView(myTextView, new LinearLayout.LayoutParams(lHeight, lWidth));
ll.addView(myEditText, new LinearLayout.LayoutParams(lHeight, lWidth));
setContentView(ll);
Creating New Views
The ability to extend existing Views, create composite widgets, and create unique new controls lets you create beautiful User Interfaces optimized for your particular workflow. Android lets you subclass the existing widget toolbox and implement your own View controls, giving you total freedom to tailor your User Interface to maximize the user experience
Modifying Existing Views
The toolbox includes a lot of common UI requirements, but the controls are necessarily generic. By customizing these basic Views, you avoid reimplementing existing behavior while still tailoring the User Interface, and functionality, of each control to your application’s needs. To create a new widget based on an existing control, create a new class that extends it — as shown in
the following skeleton code that extends TextView:
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;
public class MyTextView extends TextView {
public MyTextView (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public MyTextView (Context context) {
super(context);
}
public MyTextView (Context context, AttributeSet attrs) {
super(context, attrs);
}
}
To override the appearance or behavior of your new View, override and extend the event handlers associated
with the behavior you want to change.
In the following skeleton code, the onDraw method is overridden to modify the View’s appearance, and the onKeyDown handler is overridden to allow custom key press handling:
public class MyTextView extends TextView {
public MyTextView (Context context, AttributeSet ats, int defStyle) {
super(context, ats, defStyle);
}
public MyTextView (Context context) {
super(context);
}
public MyTextView (Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onDraw(Canvas canvas) {
[ ... Draw things on the canvas under the text ... ]
// Render the text as usual using the TextView base class.
super.onDraw(canvas);
[ ... Draw things on the canvas over the text ... ]
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
[ ... Perform some special processing ... ]
[ ... based on a particular key press ... ]
// Use the existing functionality implemented by
// the base class to respond to a key press event.
return super.onKeyDown(keyCode, keyEvent);
}
}
[Ultimate Guide][Part 5-I-2/09/2011]Android App Development - From Scrap
PART V-I
Creating Compound Controls
Compound controls are atomic, reusable widgets that contain multiple child controls laid out and wired together. When you create a compound control, you define the layout, appearance, and interaction of the Views it contains. Compound controls are created by extending a ViewGroup (usually a Layout Manager). To create a new compound control, choose a layout class that’s most suitable for positioning the child controls, and extend it as shown in the skeleton code below:
public class MyCompoundView extends LinearLayout {
public MyCompoundView(Context context) {
super(context);
}
public MyCompoundView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
As with an Activity, the preferred way to design the UI for a compound control is to use a layout resource. The following code snippet shows the XML layout definition for a simple widget consisting of an Edit Text box and a button to clear it:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
androidrientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<EditText
android:id=”@+id/editText”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
/>
<Button
android:id=”@+id/clearButton”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Clear”
/>
</LinearLayout>
To use this layout for your new widget, override its constructor to infl ate the layout resource using the inflate method from the LayoutInflate system service. The inflate method takes the layout resource and returns an infl ated View. For circumstances such as this where the returned View should be the class you’re creating, you can pass in a parent and attach the result to it automatically. The following code snippet shows the ClearableEditText class. Within the constructor it inflates the layout resource created above and gets references to each of the Views it contains. It also makes a call to hookupButton that will be used to hookup the clear text functionality when the button is pressed.
public class ClearableEditText extends LinearLayout {
EditText editText;
Button clearButton;
public ClearableEditText(Context context)
{
super(context);
String infService = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li;
li = (LayoutInflater)getContext().getSystemService(infService);
li.inflate(R.layout.clearable_edit_text, this, true);
editText = (EditText)findViewById(R.id.editText);
clearButton = (Button)findViewById(R.id.clearButton);
// Hook up the functionality
hookupButton();
}
}
If you’d prefer to construct your layout in code, you can do so just as you would for an Activity. The following code snippet shows the ClearableEditText constructor overridden to create the same UI as is defined in the XML used in the earlier example:
public ClearableEditText(Context context) {
super(context);
// Set orientation of layout to vertical
setOrientation(LinearLayout.VERTICAL);
// Create the child controls.
editText = new EditText(getContext());
clearButton = new Button(getContext());
clearButton.setText(“Clear”);
// Lay them out in the compound control.
int lHeight = LayoutParams.WRAP_CONTENT;
int lWidth = LayoutParams.FILL_PARENT;
addView(editText, new LinearLayout.LayoutParams(lWidth, lHeight));
addView(clearButton, new LinearLayout.LayoutParams(lWidth, lHeight));
hookupButton();
}
Once the screen has been constructed, you can hook up the event handlers for each child control to provide the functionality you need. In this next snippet, the hookupButton method is fi lled in to clear the textbox when the button is pressed:
private void hookupButton() {
clearButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
editText.setText(“”);
}
});
}
Creating Custom Widgets and Controls
Creating completely new Views gives you the power to fundamentally shape the way your applications look and feel. By creating your own controls, you can create User Interfaces that are uniquely suited to your users’ needs. To create new controls from a blank canvas, you extend either the View or SurfaceView classes.
The View class provides a Canvas object and a series of draw methods and Paint classes, to create a visual interface using raster graphics. You can then override user events like screen touches or key presses to provide interactivity. In situations where extremely rapid repaints and 3D graphics aren’t required, the View base class offers a powerful lightweight solution.
The SurfaceView provides a canvas that supports drawing from a background thread and using openGL for 3D graphics. This is an excellent option for graphics-heavy controls that are frequently updated or display complex graphical information, particularly games and 3D visualizations.
Creating a New Visual Interface
The base View class presents a distinctly empty 100*100 pixel square. To change the size of the control and display a more compelling visual interface, you need to override the onMeasure and onDraw methods, respectively. Within onMeasure, the new View will calculate the height and width it will occupy given a set of boundary conditions. The onDraw method is where you draw on the Canvas to create the visual interface.
public class MyView extends View {
// Constructor required for in-code creation
public MyView(Context context) {
super(context);
}
// Constructor required for inflation from resource file
public MyView (Context context, AttributeSet ats, int defaultStyle) {
super(context, ats, defaultStyle );
}
//Constructor required for inflation from resource file
public MyView (Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int wMeasureSpec, int hMeasureSpec) {
int measuredHeight = measureHeight(hMeasureSpec);
int measuredWidth = measureWidth(wMeasureSpec);
// MUST make this call to setMeasuredDimension
// or you will cause a runtime exception when
// the control is laid out.
setMeasuredDimension(measuredHeight, measuredWidth);
}
private int measureHeight(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
[ ... Calculate the view height ... ]
return specSize;
}
private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
[ ... Calculate the view width ... ]
return specSize;
}
@Override
protected void onDraw(Canvas canvas) {
[ ... Draw your visual interface ... ]
}
}
Drawing Your Control
The onDraw method is where the magic happens. If you’re creating a new widget from scratch, it’s because you want to create a completely new visual interface. The Canvas parameter available in the onDraw method is the surface you’ll use to bring your imagination to life. Android provides a variety of tools to help draw your design on the Canvas using various Paint objects.
The Canvas class includes helper methods to draw primitive 2D objects including circles, lines, rectangles, text, and Drawables (images). It also supports transformations that let you rotate, translate (move), and scale (resize) the Canvas while you draw on it.
Used in combination with Drawables and the Paint class (which offer a variety of customizable fills and pens), the complexity and detail that your control can render are limited only by the size of the screen and the power of the processor rendering it.
The following code snippet shows how to override the onDraw method to display a simple text string in the center of the control:
@Override
protected void onDraw(Canvas canvas) {
// Get the size of the control based on the last call to onMeasure.
int height = getMeasuredHeight();
int width = getMeasuredWidth();
// Find the center
int px = width/2;
int py = height/2;
// Create the new paint brushes.
// the widget’s constructor
Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(Color.WHITE);
// Define the string.
String displayText = “Hello World!”;
// Measure the width of the text string.
float textWidth = mTextPaint.measureText(displayText);
// Draw the text string in the center of the control.
canvas.drawText(displayText, px-textWidth/2, py, mTextPaint);
}
Sizing Your Control
Unless you conveniently require a control that always occupies 100 100 pixels, you will also need to override onMeasure.
The onMeasure method is called when the control’s parent is laying out its child controls. It asks the question, “How much space will you use?” and passes in two parameters — widthMeasureSpec and heightMeasureSpec. They specify the space available for the control and some metadata describing that space. Rather than return a result, you pass the View’s height and width into the setMeasuredDimension method. The following skeleton code shows how to override onMeasure. Note the calls to local method stubs calculateHeight and calculateWidth. They’ll be used to decode the widthHeightSpec and heightMeasureSpec values and calculate the preferred height and width values.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = measureHeight(heightMeasureSpec);
int measuredWidth = measureWidth(widthMeasureSpec);
setMeasuredDimension(measuredHeight, measuredWidth);
}
private int measureHeight(int measureSpec) {
// Return measured widget height.
}
private int measureWidth(int measureSpec) {
// Return measured widget width.
}
The boundary parameters, widthMeasureSpec and heightMeasureSpec, are passed in as integers for efficiency reasons. Before they can be used, they first need to be decoded using the static getMode and getSize methods from the MeasureSpec class, as shown in the snippet below:
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
Depending on the mode value, the size represents either the maximum space available for the control (in the case of AT_MOST), or the exact size that your control will occupy (for EXACTLY). In the case of UNSPECIFIED, your control does not have any reference for what the size represents. By marking a measurement size as EXACT, the parent is insisting that the View will be placed into an area of the exact size specifi ed. The AT_MOST designator says the parent is asking what size the View would like to occupy, given an upper bound. In many cases, the value you return will be the same. In either case, you should treat these limits as absolute. In some circumstances, it may still be appropriate to return a measurement outside these limits, in which case you can let the parent choose how to deal with the oversized View, using techniques such as clipping and scrolling. The following skeleton code shows a typical implementation for handling View measurement:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = measureHeight(heightMeasureSpec);
int measuredWidth = measureWidth(widthMeasureSpec);
setMeasuredDimension(measuredHeight, measuredWidth);
}
private int measureHeight(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST) {
// Calculate the ideal size of your
// control within this maximum size.
// If your control fills the available
// space return the outer bound.
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY) {
// If your control can fit within these bounds return that value.
result = specSize;
}
return result;
}
private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST) {
// Calculate the ideal size of your control
// within this maximum size.
// If your control fills the available space
// return the outer bound.
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY) {
// If your control can fit within these bounds return that value.
result = specSize;
}
return result;
}
Handling User Interaction Events
To make your new widget interactive, it will need to respond to user events like key presses, screen touches, and button clicks. Android exposes several virtual event handlers, listed below, that let you react to user input:
❑ onKeyDown Called when any device key is pressed; includes the D-pad, keyboard, hang-up, call, back, and camera buttons
❑ onKeyUp Called when a user releases a pressed key
❑ onTrackballEvent Called when the device’s trackball is moved
❑ onTouchEvent Called when the touch screen is pressed or released, or it detects movement
The following code snippet shows a skeleton class that overrides each of the user interaction handlers in a View:
@Override
public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
// Return true if the event was handled.
return true;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
// Return true if the event was handled.
return true;
}
@Override
public boolean onTrackballEvent(MotionEvent event ) {
// Get the type of action this event represents
int actionPerformed = event.getAction();
// Return true if the event was handled.
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Get the type of action this event represents
int actionPerformed = event.getAction();
// Return true if the event was handled.
return true;
}
Introducing the Android Menu System
To improve the usability of application menus, Android features a three-stage menu system optimized for small screens:
❑ The Icon Menu This compact menu appears along the bottom of the screen when the Menu button is pressed. It displays the icons and text for up to six Menu Items (or submenus). This icon menu does not display checkboxes, radio buttons, or the shortcut keys for Menu Items, so it’s generally good practice not to assign checkboxes or radio buttons to icon menu items, as
they will not be available. If more than six Menu Items have been defined, a More item is included that, when selected, displays
the expanded menu. Pressing the Back button closes the icon menu.
❑ The Expanded Menu The expanded menu is triggered when a user selects the More Menu Item from the icon menu. The expanded menu displays a scrollable list of only the Menu Items that weren’t visible in the icon menu. This menu displays full text, shortcut keys, and checkboxes/radio buttons as appropriate.
❑ Submenus The traditional “expanding hierarchical tree” can be awkward to navigate using a mouse, so it’s no surprise that this metaphor is particularly ill-suited for use on mobile devices. The Android alternative is to display each submenu in a floating window.
Defining an Activity Menu
To define a menu for an Activity, override its onCreateOptionsMenu method. This method is triggered the first time an Activity’s menu is displayed. The onCreateOptionsMenu receives a Menu object as a parameter. You can store a reference to, and continue to use, the Menu reference elsewhere in your code until the next time that onCreateOptionsMenu is called. You should always call through to the base implementation as it automatically includes additional system
menu options where appropriate.
Use the add method on the Menu object to populate your menu. For each Menu Item, you must specify:
❑ A group value to separate Menu Items for batch processing and ordering
❑ A unique identifi er for each Menu Item. For efficiency reasons, Menu Item selections are generally handled by the onOptionsItemSelected event handler, so this unique identifier is important to determine which Menu Item was pressed. It is convention to declare each menu ID as a private static variable within the Activity class. You can use the Menu.FIRST static constant and simply increment that value for each subsequent item.
❑ An order value that defines the order in which the Menu Items are displayed
❑ The menu text, either as a character string or as a string resource
When you have finished populating the menu, return True to allow Android to display the menu. The following skeleton code shows how to add a single item to an Activity menu:
static final private int MENU_ITEM = Menu.FIRST;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// Group ID
int groupId = 0;
// Unique menu item identifier. Used for event handling.
int menuItemId = MENU_ITEM;
// The order position of the item
int menuItemOrder = Menu.NONE;
// Text to be displayed for this menu item.
int menuItemText = R.string.menu_item;
// Create the menu item and keep a reference to it.
MenuItem menuItem = menu.add(groupId, menuItemId,
menuItemOrder, menuItemText);
return true;
}
Like the Menu object, each Menu Item reference returned by a call to add is valid until the next call to onCreateOptionsMenu. Rather than maintaining a reference to each item, you can find a particular. Menu Item by passing its ID into the Menu.findItem method.
Menu Item Options
Android supports most of the traditional Menu Item options you’re probably familiar with, including icons, shortcuts, checkboxes, and radio buttons, as described below:
❑ Checkboxes and Radio Buttons Checkboxes and radio buttons on Menu Items are visible in expanded menus and submenus. To set a Menu Item as a checkbox, use the setCheckable method. The state of that checkbox is controlled using setChecked. A radio button group is a group of items displaying circular buttons, where only one item can be selected at any given time. Checking one of these items will automatically unselect any other item in the same group, that is currently checked. To create a radio button group, assign the same group identifier to each item, then call Menu.setGroupCheckable, passing in that group identifier and setting the exclusive parameter to True. Checkboxes are not visible in the icon menu, so Menu Items that feature checkboxes should be reserved for submenus and items that appear only in the expanded menu. The following code snippet shows how to add a checkbox and a group of three radio buttons:
// Create a new check box item.
menu.add(0, CHECKBOX_ITEM, Menu.NONE, “CheckBox”).setCheckable(true);
// Create a radio button group.
menu.add(RB_GROUP, RADIOBUTTON_1, Menu.NONE, “Radiobutton 1”);
menu.add(RB_GROUP, RADIOBUTTON_2, Menu.NONE, “Radiobutton 2”);
menu.add(RB_GROUP, RADIOBUTTON_3, Menu.NONE,
“Radiobutton 3”).setChecked(true);
menu.setGroupCheckable(RB_GROUP, true, true);
❑ Shortcut Keys You can specify a keypad shortcut for a Menu Item using the setShortcut method. Each call to setShortcut requires two shortcut keys, one for use with the numeric keypad and a second to support a full keyboard. Neither key is case-sensitive. The code below shows how to set a shortcut for both modes:
menuItem.setShortcut(‘0’, ‘b’);
❑ Condensed Titles The icon menu does not display shortcuts or checkboxes, so it’s often necessary to modify its display text to indicate its state. The following code shows how to set the icon menu–specific text for a Menu Item:
menuItem.setTitleCondensed(“Short Title”);
❑ Icons Icon is a drawable resource identifier for an icon to be used in the Menu Item. Icons are only displayed in the icon menu; they are not visible in the extended menu or submenus. The following snippet shows how to apply an icon to a Menu Item:
menuItem.setIcon(R.drawable.menu_item_icon);
❑ Menu Item Click Listener An event handler that will execute when the Menu Item is selected. For efficiency reasons, this is discouraged; instead, Menu Item selections should be handled by the onOptionsItemSelected handler as shown later in this section. To apply a click listener to a Menu Item, use the pattern shown in the following code snippet:
menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem _menuItem) {
return true;
}
});
[Ultimate Guide][Part 5-III][2/09/2011]Android App Development - From Scratc
PART V-II
Dynamically Updating Menu Items
By overriding your activity’s onPrepareOptionsMenu method, you can modify your menu based on the application state each time it’s displayed. This lets you dynamically disable/enable each item, set visibility, and modify text at runtime.
To modify Menu Items dynamically, you can either keep a reference to them when they’re created in the onCreateOptionsMenu method, or you can use menu.findItem as shown in the following skeleton
code, where onPrepareOptionsMenu is overridden:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem menuItem = menu.findItem(MENU_ITEM);
[ ... modify menu items ... ]
return true;
}
Handling Menu Selections
Android handles all of an Activity’s Menu Item selections using a single event handler, the onOptions ItemSelected method. The Menu Item selected is passed in to this method as the Menu Item parameter. To react to the menu selection, compare the item.getItemId value to the Menu Item identifi ers you used when populating the menu, and react accordingly, as shown in the following code:
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
// Find which menu item has been selected
switch (item.getItemId()) {
// Check for each known menu item
case (MENU_ITEM):
[ ... Perform menu handler actions ... ]
return true;
}
// Return false if you have not handled the menu item.
return false;
}
Creating Submenus
Submenus are displayed as regular Menu Items that, when selected, reveal more items. Traditionally, submenus are displayed using a hierarchical tree layout. Android uses a different approach to simplify menu navigation for small-screen devices. Rather than a tree structure, selecting a submenu presents a single floating window that displays all of its Menu Items. You can add submenus using the addSubMenu method. It supports the same parameters as the add method used to add normal Menu Items, allowing you to specify a group, unique identifi er, and text string for each submenu. You can also use the setHeaderIcon and setIcon methods to specify an icon to display in the submenu’s header bar or the regular icon menu, respectively. The Menu Items within a submenu support the same options as those assigned to the icon or extended menus. However, unlike traditional systems, Android does not support nested submenus. The code snippet below shows an extract from an implementation of the onCreateMenuOptions code that adds a submenu to the main menu, sets the header icon, and then adds a submenu Menu Item:
SubMenu sub = menu.addSubMenu(0, 0, Menu.NONE, “Submenu”);
sub.setHeaderIcon(R.drawable.icon);
sub.setIcon(R.drawable.icon);
MenuItem submenuItem = sub.add(0, 0, Menu.NONE, “Submenu Item”);
Using Context Menus
Context Menus are contextualized by the currently focused View and are triggered by pressing the trackball, middle D-pad button, or the View for around 3 seconds. You define and populate Context Menus similarly to the Activity menu. There are two options available for creating Context Menus for a particular View.
Creating Context Menus
The first option is to create a generic Context Menu for a View class by overriding a View’s onCreate ContextMenu handler:
@Override
public void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
menu.add(“ContextMenuItem1”);
}
The more common alternative is to create Activity-specific Context Menus by overriding the onCreate ContextMenu method and registering the Views that should use it. Register Views using register ForContextMenu and passing them in, as shown in the following code:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
EditText view = new EditText(this);
setContentView(view);
registerForContextMenu(view);
}
Once a View has been registered, the onCreateContextMenu handler will be triggered whenever a Context Menu should be displayed for that View. Override onCreateContextMenu and check which View has triggered the menu creation to populate the menu parameter with the appropriate contextual items:
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.setHeaderTitle(“Context Menu”);
menu.add(0, menu.FIRST, Menu.NONE,
“Item 1”).setIcon(R.drawable.menu_item);
menu.add(0, menu.FIRST+1, Menu.NONE, “Item 2”).setCheckable(true);
menu.add(0, menu.FIRST+2, Menu.NONE, “Item 3”).setShortcut(‘3’, ‘3’);
SubMenu sub = menu.addSubMenu(“Submenu”);
sub.add(“Submenu Item”);
}
As shown above, the ContextMenu class supports the same add method as the Menu class, so you can populate a Context Menu in the same way as Activity menus — including support for submenus — but icons will never be displayed. You can also specify the title and icon to display in the Context Menu’s header bar. Android supports late runtime population of Context Menus using Intent Filters. This mechanism lets you populate a Context Menu by specifying the kind of data presented by the current View, and asking other Android applications if they support any actions for it. The most common example of this behavior is the cut/copy/paste Menu Items available on EditText controls.
Handling Context Menu Selections
Context Menu Item selections are handled similarly to the Activity menu. You can attach an Intent or Menu Item Click Listener directly to each Menu Item, or use the preferred technique of overriding the onContextItemSelected method on the Activity.
This event handler is triggered whenever a Context Menu Item is selected within the Activity. A skeleton implementation is shown below:
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
[ ... Handle menu item selection ... ]
return false;
}
[Ultimate Guide][Part 6-released][3/09/2011]Android App Development - From Scratc
PART-VI
Intents
Intents are used as a message-passing mechanism that lets you declare your intention that an action be performed, usually with a particular piece of data. One of the most common uses for Intents is to start new Activities, either explicitly (by specifying the class to load) or implicitly (by requesting an action be performed on a piece of data). Intents can also be used to broadcast messages across the system. Any application can register a Broadcast Receiver to listen for, and react to, these broadcast Intents. This lets you create event-driven applications based on internal, system, or third-party application events.
Using Intents to Launch Activities
The most common use of Intents is to bind your application components. Intents are used to start, stop, and transition between the Activities within an application.
To open a different application screen (Activity) in your application, call startActivity, passing in an Intent, as shown in the snippet below.
startActivity(myIntent);
The Intent can either explicitly specify the class to open, or include an action that the target should perform. In the latter case, the run time will choose the Activity to open, using a process known as “Intent resolution.” The startActivity method finds, and starts, the single Activity that best matches your Intent. When using startActivity, your application won’t receive any notification when the newly launched Activity finishes. To track feedback from the opened form, use the startActivityForResult method.
Explicitly Starting New Activities
To explicitly select an Activity class to start, create a new Intent specifying the current application context and the class of the Activity to launch. Pass this Intent in to startActivity, as shown in the following code snippet:
Intent intent = new Intent(MyActivity.this, MyOtherActivity.class);
startActivity(intent);
After calling startActivity, the new Activity (in this example, MyOtherActivity) will be created and become visible and active, moving to the top of the Activity stack. Calling finish programmatically on the new Activity will close it and remove it from the stack. Alternatively, users can navigate to the previous Activity using the device’s Back button.
Implicit Intents and Late Runtime Binding
Implicit Intents are a mechanism that lets anonymous application components service action requests. When constructing a new implicit Intent to use with startActivity, you nominate an action to perform and, optionally, supply the data on which to perform that action. When you use this new implicit Intent to start an Activity, Android will — at run time — resolve it into the class best suited to performing the action on the type of data specifi ed. This means that you can create projects that use functionality from other applications, without knowing exactly which application you’re borrowing functionality from ahead of time. For example, if you want to let users make calls from an application, rather than implementing a new dialer you could use an implicit Intent that requests that the action (“dial a number”) be performed on a phone number (represented as a URI), as shown in the code snippet below:
{
Intent intent = new Intent(Intent.ACTION_DIAL,
Uri.parse(“tel:555-2368”));
startActivity(intent);
}
Android resolves this Intent and starts an Activity that provides the dial action on a telephone number in this case, the dialler Activity.
Introducing Linkify
Linkify is a helper class that automagically creates hyperlinks within TextView classes through RegEx pattern matching. Text that matches a specified RegEx pattern will be converted into a clickable hyperlink that implicitly fires startActivity(new Intent (Intent.ACTION_VIEW, uri)) using the matched text as the target URI. You can specify any string pattern you want to turn into links; for convenience, the Linkify class provides presets for common content types (like phone numbers and e-mail/web addresses).
The Native Link Types
The static Linkify.addLinks method accepts the View to linkify, and a bitmask of one or more of the default content types supported and supplied by the Linkify class: WEB_URLS, EMAIL_ADDRESSES, PHONE_NUMBERS, and ALL. The following code snippet shows how to linkify a TextView to display web and e-mail addresses as hyperlinks. When clicked, they will open the browser or e-mail application, respectively.
TextView textView = (TextView)findViewById(R.id.myTextView);
Linkify.addLinks(textView, Linkify.WEB_URLS|Linkify.EMAIL_ADDRESSES);[/B]
You can linkify Views from within a layout resource using the android:autoLink attribute. It supports one or more (separated by |) of the following self-describing values: none, web, email, phone, or all. The following XML snippet shows how to add hyperlinks for phone numbers and e-mail addresses:
<TextView
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:text=”@string/linkify_me”
android:autoLink=”phone|email”
/>
Creating Custom Link Strings
To defi ne your own linkify strings, you create a new RegEx pattern to match the text you want to display as hyperlinks. As with the native types, you linkify the target view by calling Linkify.addLinks, but this time pass in the new RegEx pattern. You can also pass in a prefix that will be prepended to the target URI when a link is clicked.
The following example shows a View being linkified to support earthquake data provided by an Android Content Provider. Rather than include the entire schema, the linkify pattern matches any text that starts with “quake” and is followed by a number. The content schema is then prepended to the URI before the Intent is fired.
int flags = Pattern.CASE_INSENSITIVE;
Pattern p = Pattern.compile(“\\bquake[0-9]*\\b”, flags);
Linkify.addLinks(myTextView, p,
“content://com.paad.earthquake/earthquakes/”);
Linkify also supports TransformFilter and MatchFilter interfaces. They offer additional control over the target URI structure and the definition of matching strings, and are used as shown in the skeleton code below:
Linkify.addLinks(myTextView, pattern, prefixWith,
new MyMatchFilter(), new MyTransformFilter());
Using the Match Filter
Implement the acceptMatch method in your MatchFilter to add additional conditions to RegEx pattern matches. When a potential match is found, acceptMatch is triggered, with the match start and end index passed in as parameters. The following code shows a MatchFilter implementation that cancels any match that is immediately preceded by an exclamation mark.
{
public boolean acceptMatch(CharSequence s, int start, int end) {
return (start == 0 || s.charAt(start-1) != ‘!’);
}
}
Using the Transform Filter
The Transform Filter gives you more freedom to format your text strings by letting you modify the implicit URI generated by the link text. Decoupling the link text from the target URI gives you more freedom in how you display data strings to your users. To use the Transform Filter, implement the transformUrl method in your Transform Filter. When Linkify fi nds a successful match, it calls transformUrl, passing in the RegEx pattern used and the default URI string it creates. You can modify the matched string, and return the URI as a target suitable to be “viewed” by another Android application. The following TransformFilter implementation transforms the matched text into a lowercase URI:
class MyTransformFilter implements TransformFilter {
public String transformUrl(Matcher match, String url) {
return url.toLowerCase();
}
}
Returning Results from Activities
An Activity started using startActivity is independent of its parent and will not provide any feedback when it closes. Alternatively, you can start an Activity as a sub-Activity that’s inherently connected to its parent. Sub- Activities trigger an event handler within their parent Activity when they close. Sub-Activities are perfect for situations in which one Activity is providing data input (such as a user selecting an item from a list) for another. Sub-Activities are created the same way as normal Activities and must also be registered in the application manifest. Any manifest-registered Activity can be opened as a sub-Activity.
Launching Sub-Activities
The startActivityForResult method works much like startActivity but with one important difference. As well as the Intent used to determine which Activity to launch, you also pass in a request code. This value will be used later to uniquely identify the sub-Activity that has returned a result. The skeleton code for launching a sub-Activity is shown below:
private static final int SHOW_SUBACTIVITY = 1;
Intent intent = new Intent(this, MyOtherActivity.class);
startActivityForResult(intent, SHOW_SUBACTIVITY);
As with regular Activities, sub-Activities can be started implicitly or explicitly. The following skeleton code uses an implicit Intent to launch a new sub-Activity to pick a contact:
private static final int PICK_CONTACT_SUBACTIVITY = 2;
Uri uri = Uri.parse(“content://contacts/people”);
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, PICK_CONTACT_SUBACTIVITY);
Returning Results
When your sub-Activity is ready to close, call setResult before finish to return a result to the calling Activity. The setResult method takes two parameters: the result code and result payload represented as an Intent. The result code is the “result” of running the sub-Activity — generally either Activity.RESULT_OK or Activity.RESULT_CANCELED. In some circumstances, you’ll want to use your own response codes to handle application-specifi c choices; setResult supports any integer value. The Intent returned as a result can include a URI to a piece of content (such as the contact, phone number, or media fi le) and a collection of Extras used to return additional information. This next code snippet is taken from a sub-Activity’s onCreate method and shows how an OK button and a Cancel button might return different results to the calling Activity:
Button okButton = (Button) findViewById(R.id.ok_button);
okButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Uri data = Uri.parse(“content://horses/” + selected_horse_id);
Intent result = new Intent(null, data);
result.putExtra(IS_INPUT_CORRECT, inputCorrect);
result.putExtra(SELECTED_PISTOL, selectedPistol);
setResult(RESULT_OK, result);
finish();
}
});
Button cancelButton = (Button) findViewById(R.id.cancel_button);
cancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_CANCELED, null);
finish();
}
});
Handling Sub-Activity Results
When a sub-Activity closes, its parent Activity’s onActivityResult event handler is fi red. Override this method to handle the results from the sub-Activities. The onActivityResult handler receives several parameters:
❑ The Request Code The request code that was used to launch the returning sub-Activity
❑ A Result Code The result code set by the sub-Activity to indicate its result. It can be any integer value, but typically will be either Activity.RESULT_OK or Activity.RESULT_CANCELLED. If the sub-Activity closes abnormally or doesn’t specify a result code before it closes, the result code is Activity.RESULT_CANCELED.
❑ Data An Intent used to bundle any returned data. Depending on the purpose of the sub-Activity, it will typically include a URI that represents the particular piece of data selected from a list. Alternatively, or additionally, the sub-Activity can return extra information as primitive values using the “extras” mechanism. The skeleton code for implementing the onActivityResult event handler within an Activity is shown below:
private static final int SHOW_SUB_ACTIVITY_ONE = 1;
private static final int SHOW_SUB_ACTIVITY_TWO = 2;
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (SHOW_SUB_ACTIVITY_ONE) : {
if (resultCode == Activity.RESULT_OK) {
Uri horse = data.getData();
boolean inputCorrect = data.getBooleanExtra(IS_INPUT_CORRECT,
false);
String selectedPistol = data.getStringExtra(SELECTED_PISTOL);
}
break;
}
case (SHOW_SUB_ACTIVITY_TWO) : {
if (resultCode == Activity.RESULT_OK) {
// TODO: Handle OK click.
}
break;
}
}
}
Native Android Actions
Native Android applications also use Intents to launch Activities and sub-Activities. The following non comprehensive list shows some of the native actions available as static string constants in the Intent class. You can use these actions when creating implicit Intents to start Activities and sub-Activities within your own applications.
❑ ACTION_ANSWER Opens an Activity that handles incoming calls. Currently this is handled by the native phone dialer.
❑ ACTION_CALL Brings up a phone dialer and immediately initiates a call using the number supplied in the data URI. Generally, it’s considered better form to use the Dial_Action if possible.
❑ ACTION_DELETE Starts an Activity that lets you delete the entry currently stored at the data URI location.
❑ ACTION_DIAL Brings up a dialer application with the number to dial prepopulated from the data URI. By default, this is handled by the native Android phone dialer. The dialer can normalize most number schemas.
❑ ACTION_EDIT Requests an Activity that can edit the data at the URI.
❑ ACTION_INSERT Opens an Activity capable of inserting new items into the cursor specified in the data field. When called as a sub-Activity, it should return a URI to the newly inserted item.
❑ ACTION_PICK Launches a sub-Activity that lets you pick an item from the URI data. When closed, it should return a URI to the item that was picked. The Activity launched depends on the data being picked; for example, passing ontent://contacts/people will invoke the native contacts list.
❑ ACTION_SEARCH Launches the UI for performing a search. Supply the search term as a string in the Intent’s extras using the SearchManager.QUERY key.
❑ ACTION_SENDTO Launches an Activity to send a message to the contact specified by the data URI.
❑ ACTION_SEND Launches an Activity that sends the specifi ed data (the recipient needs to be selected by the resolved Activity). Use setType to set the Intent’s type as the transmitted data’s mime type. The data itself should be stored as an extra using the key EXTRA_TEXT or EXTRA_STREAM depending on the type. In the case of e-mail, the native Android applications will also accept extras using the EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, and EXTRA_SUBJECT keys.
❑ ACTION_VIEW The most common generic action. View asks that the data supplied in the Intent’s URI be viewed in the most reasonable manner. Different applications will handle view requests depending on the URI schema of the data supplied. Natively, http: addresses will open in the browser, tel: addresses will open the dialer to call the number, geo: addresses are displayed in the Google Maps application, and contact content will be displayed in the Contact Manager.
❑ ACTION_WEB_SEARCH Opens an activity that performs a Web search based on the text supplied in the data URI.
Using Intent Filters to Service Implicit Intents
If an Intent is a request for an action to be performed on a set of data, how does Android know which application (and component) to use to service the request? Intent Filters are used to register Activities, Services, and Broadcast Receivers as being capable of performing an action on a particular kind of data. Using Intent Filters, application components tell Android that they can service action requests from others, including components in the same, native, or third-party applications. To register an application component as an Intent handler, use the intent-filter tag within the component’s manifest node. Using the following tags (and associated attributes) within the Intent Filter node, you can specify a component’s supported actions, categories, and data:
❑ action Use the android:name attribute to specify the name of the action being serviced. Actions should be unique strings, so best practice is to use a naming system based on the Java package naming conventions.
❑ category Use the android:category attribute to specify under which circumstances the action should be serviced. Each Intent Filter tag can include multiple category tags. You can specify your own categories or use the standard values provided by Android and listed below:
1. ALTERNATIVE one of the uses of Intent Filters is to help populate context menus with actions. The alternative category specifies that this action should be available as an alternative to the default action performed on an item of this data type. For example, where the default action for a contact is to view it, the alternatives could be to edit or delete it.
2. SELECTED_ALTERNATIVE Similar to the alternative category, but where Alternative will always resolve to a single action using the Intent resolution described below, SELECTED_ALTERNATIVE is used when a list of possibilities is required.
3. BROWSABLE Specifies an action available from within the browser. When an Intent is fi red from within the browser, it will always specify the browsable category.
4. DEFAULT Set this to make a component the default action for the data values defined by the Intent Filter. This is also necessary for Activities that are launched using an explicit Intent.
5. GADGET By setting the gadget category, you specify that this Activity can run embedded inside another Activity.
6. HOME The home Activity is the fi rst Activity displayed when the device starts (the launch screen). By setting an Intent Filter category as home without specifying an action, you are presenting it as an alternative to the native home screen.
7. LAUNCHER Using this category makes an Activity appear in the application launcher.
❑ data The data tag lets you specify matches for data your component can act on; you can include several schemata if your component is capable of handling more than one. You can use any combination of the following attributes to specify the data that your component supports:
1. android:host Specifi es a valid host name (e.g., com.google).
2. android:mimetype Lets you specify the type of data your component is capable of handling.
❑ androidath Valid “path” values for the URI (e.g., /transport/boats/)
❑ androidort Valid ports for the specifi ed host
❑ android:scheme Requires a particular scheme (e.g., content or http).
[Ultimate Guide][Part 6-II-released][3/09/2011]Android App Development - From Scratch
PART VI-II
Introducing Adapters
Adapters are bridging classes that bind data to user-interface Views. The adapter is responsible for creating the child views used to represent each item and providing access to the underlying data. User-interface controls that support Adapter binding must extend the AdapterView abstract class. It’s possible to create your own AdapterView-derived controls and create new Adapter classes to bind them.
Introducing Some Android-Supplied Adapters
In many cases, you won’t have to create your own Adapter from scratch. Android supplies a set of Adapters that pump data into the native user-interface widgets. Because Adapters are responsible both for supplying the data and selecting the Views that represent each item, Adaptors can radically modify the appearance and functionality of the controls they’re bound to. The following list highlights two of the most useful and versatile native adapters:
❑ ArrayAdapter The ArrayAdapter is a generic class that binds Adapter Views to an array of objects. By default, the ArrayAdapter binds the toString value of each object to a TextView control defined within a layout. Alternative constructors llow you to use more complex layouts, or you can extend the class to use alternatives to Text View (such as populating an
ImageView or nested layout) by overriding the getView method.
❑ SimpleCursorAdapter The SimpleCursorAdapter binds Views to cursors returned from Content Provider queries. You specify an XML layout defi nition and then bind the value within each column in the result set, to a View in that layout.
Using Adapters for Data Binding
To apply an Adapter to an AdapterView-derived class, you call the View’s setAdapter method, passing in an Adapter instance, as shown in the snippet below:
ArrayList<String> myStringArray = new ArrayList<String>();
ArrayAdapter<String> myAdapterInstance;
int layoutID = android.R.layout.simple_list_item_1;
myAdapterInstance = new ArrayAdapter<String>(this, layoutID, myStringArray);
myListView.setAdapter(myAdapterInstance);
Using Internet Resources
With Internet connectivity and WebKit browser, you might well ask if there’s any reason to create native Internet-based applications when you could make a web-based version instead.
There are several benefits to creating thick- and thin-client native applications rather than relying on entirely web-based solutions:
❑ Bandwidth Static resources like images, layouts, and sounds can be expensive data consumers on devices with limited and often expensive bandwidth restraints. By creating a native application, you can limit the bandwidth requirements to only data updates.
❑ Caching Mobile Internet access has not yet reached the point of ubiquity. With a browserbased solution, a patchy Internet connection can result in intermittent application availability. A native application can cache data to provide as much functionality as possible without a live connection.
❑ Native Features Android devices are more than a simple platform for running a browser; they include location-based services, camera hardware, and accelerometers. By creating a native application, you can combine the data available online with the hardware features available on the device to provide a richer user experience.
Connecting to an Internet Resource
While the details of working with specific web services aren’t covered within this book, it’s useful to know the general principles of connecting to the Internet and getting an input stream from a remote data source. Before you can access Internet resources, you need to add an INTERNET uses-permission node to your application manifest, as shown in the following XML snippet:
<uses-permission android:name=”android.permission.INTERNET”/>
The following skeleton code shows the basic pattern for opening an Internet data stream:
String myFeed = getString(R.string.my_feed);
try {
URL url = new URL(myFeed);
URLConnection connection = url.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection)connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream in = httpConnection.getInputStream();
[ ... Process the input stream as required ... ]
}
}
catch (MalformedURLException e) { }
catch (IOException e) { }
Android includes several classes to help you handle network communications. They are available in the java.net.* and android.net.* packages.
Leveraging Internet Resources
Android offers several ways to leverage Internet resources. At one extreme, you can use the WebView widget to include a WebKit-based browser control within an Activity. At the other extreme, you can use client-side APIs such as Google’s GData APIs to interact directly with server processes. Somewhere in between, you can process remote XML feeds to extract and process data using a Java-based XML parser such as SAX or javax. Detailed instructions for parsing XML and interacting with specific web services are outside the scope of this book. That said, the Earthquake example, included later in this chapter, gives a fully worked example of parsing an XML feed using the javax classes. If you’re using Internet resources in your application, remember that your users’ data connections depend on the communications technology available to them. EDGE and GSM connections are notoriously low bandwidth, while a Wi-Fi connection may be unreliable in a mobile setting. Optimize the user experience by limiting the quantity of data being transmitted, and ensure that your application is robust enough to handle network outages and bandwidth limitations.
Introducing Dialogs
Dialog boxes are a common UI metaphor in desktop and web applications. They’re used to help users answer questions, make selections, confirm actions, and read warning or error messages. An Android Dialog is a floating window that partially obscures the Activity that launched it.
There are three ways to implement a Dialog box in Android:
❑ Using a Dialog-Class Descendent As well as the general-purpose AlertDialog class, Android includes several specialist classes that extend Dialog. Each is designed to provide specific Dialog-box functionality. Dialog-class-based screens are constructed entirely within their calling Activity, so they don’t need to be registered in the manifest, and their life cycle is controlled entirely by the calling Activity.
❑ Dialog-Themed Activities You can apply the Dialog theme to a regular Activity to give it the appearance of a Dialog box.
❑ Toasts Toasts are special non-modal transient message boxes, often used by Broadcast Receivers and background services to notify users of events.
Introducing the Dialog Class
The Dialog class implements a simple floating window that is constructed entirely within an Activity. To use the base Dialog class, you create a new instance and set the title and layout as shown below:
Dialog d = new Dialog(MyActivity.this);
// Have the new window tint and blur the window it
// obscures.
Window window = d.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
d.setTitle(“Dialog Title”);
d.setContentView(R.layout.dialog_view);
TextView text = (TextView)d.findViewById(R.id.dialogTextView);
text.setText(“This is the text in my dialog”);
The AlertDialog Class
The AlertDialog class is one of the most versatile Dialog implementations. It offers various options that let you construct screens for some of the most common Dialog-box use cases, including:
1. Presenting a message to the user offering one to three options in the form of alternative buttons. This functionality is probably familiar to you if you¡¯ve done any desktop programming, where the buttons presented are usually a selection of OK, Cancel, Yes, or No.
2. . Offering a list of options in the form of check buttons or radio buttons.
3. . Providing a text entry box for user input.
To construct the Alert Dialog user interface, create a new AlertDialog.Builder object, as shown below:
AlertDialog.Builder ad = new AlertDialog.Builder(context);
You can then assign values for the title and message to display, and optionally assign values to be used for any buttons, selection items, and text input boxes you wish to display. That includes setting event listeners to handle user interaction.
The following code gives an example of a new Alert Dialog used to display a message and offer two button options to continue. Clicking on either button will automatically close the Dialog after executing the attached Click Listener.
Context context = MyActivity.this;
String title = ¡°It is Pitch Black¡±;
String message = ¡°You are likely to be eaten by a grue.¡±;
String button1String = ¡°Go Back¡±;
String button2String = ¡°Move Forward¡±;
AlertDialog.Builder ad = new AlertDialog.Builder(context);
ad.setTitle(title);
ad.setMessage(message);
ad.setPositiveButton(button1String,
new OnClickListener() {
public void onClick(DialogInterface dialog,
int arg1) {
eatenByGrue();
}
});
ad.setNegativeButton(button2String,
new OnClickListener(){
public void onClick(DialogInterface dialog,
int arg1) {
// do nothing
}
});
ad.setCancelable(true);
ad.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
eatenByGrue();
}
});
Specialist Input Dialogs
One of the major uses of Dialog boxes is to provide an interface for user input. Android includes several specialist Dialog boxes that encapsulate controls designed to facilitate common user input requests. They include the following:
❑ DatePickerDialog Lets users select a date from a DatePicker View. The constructor includes a callback listener to alert your calling Activity when the date has been set.
❑ TimePickerDialog Similar to the DatePickerDialog, this Dialog lets users select a time from a TimePicker View.
❑ ProgressDialog A Dialog that displays a progress bar beneath a message textbox. Perfect for keeping the user informed of the ongoing progress of a time-consuming operation.
Using and Managing Dialogs
Rather than creating new instances of a Dialog each time it’s required, Android provides theOnCreateDialog and onPrepareDialog event handlers within the Activity class to persist and manage Dialog-box instances. By overriding the onCreateDialog class, you can specify Dialogs that will be created on demand when showDialog is used to display a specific Dialog. As shown in this code snippet, the overridden method includes a switch statement that lets you determine which Dialog is required:
static final private int TIME_DIALOG = 1;
@Override
public Dialog onCreateDialog(int id) {
switch(id) {
case (TIME_DIALOG) :
AlertDialog.Builder timeDialog = new AlertDialog.Builder(this);
timeDialog.setTitle(“The Current Time Is...”);
timeDialog.setMessage(“Now”);
return timeDialog.create();
}
return null;
}
After the initial creation, each time a showDialog is called, it will trigger the onPrepareDialog handler. By overriding this method, you can modify a Dialog immediately before it is displayed. This lets you contextualize any of the display values, as shown in the following snippet, which assigns the current time to the Dialog created above:
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case (TIME_DIALOG) :
SimpleDateFormat sdf = new SimpleDateFormat(“HH:mm:ss”);
Date currentTime;
currentTime = new Date(java.lang.System.currentTimeMillis());
String dateString = sdf.format(currentTime);
AlertDialog timeDialog = (AlertDialog)dialog;
timeDialog.setMessage(dateString);
break;
}
}
Once you’ve overridden these methods, you can display the Dialogs by calling showDialog, as shown below. Pass in the identifi er for the Dialog you wish to display, and Android will create (if necessary) and prepare the Dialog before displaying it:
showDialog(TIME_DIALOG);
As well as improving resource use, this technique lets your Activity handle the persistence of state information within Dialogs. Any selection or data input (such as item selection and text entry) will be persisted between displays of each Dialog instance.
Using Activities as Dialogs
Dialogs offer a simple and lightweight technique for displaying screens, but there will still be times when you need more control over the content and life cycle of your Dialog box. The solution is to implement it as a full Activity. By creating an Activity, you lose the lightweight nature of the Dialog class, but you gain the ability to implement any screen you want and full access to the Activity life-cycle event handlers.
So, when is an Activity a Dialog? The easiest way to make an Activity look like a Dialog is to apply the android:style/ Theme.Dialog theme when you add it to your manifest, as shown in the following XML snippet:
<activity android:name=”MyDialogActivity”
android:theme=”@android:style/Theme.Dialog”>
</activity>
This will cause your Activity to behave like a Dialog, floating on top of, and partially obscuring, the Activity beneath it.
[Ultimate Guide][Part 7-released][5/09/2011]Android App Development - From Scratch
PART-VII
Android Techniques for Saving Data
The data persistence techniques in Android provide options for balancing speed, efficiency, and robustness:
❑ Shared Preferences When storing the UI state, user preferences, or application settings, you want a lightweight mechanism to store a known set of values. Shared Preferences let you save groups of key/value pairs of primitive data as named preferences.
❑ Files It’s not pretty, but sometimes writing to, and reading from, files directly is the only way to go. Android lets you create and load fi les on the device’s internal or external media.
❑ SQLite Databases When managed, structured data is the best approach, Android offers the SQLite relational database library. Every application can create its own databases over which it has total control.
❑ Content Providers Rather than a storage mechanism in their own right, Content Providers let you expose a well-defined interface for using and sharing private data. You can control access to Content Providers using the standard permission system.
Saving Simple Application Data
There are two lightweight techniques for saving simple application data for Android applications — Shared Preferences and a pair of event handlers used for saving Activity instance details. Both mechanisms use a name/value pair (NVP) mechanism to store simple primitive values. Using SharedPreferences, you can create named maps of key/value pairs within your application
that can be shared between application components running in the same Context. Shared Preferences support the primitive types Boolean, string, float, long, and integer, making the ideal way to quickly store default values, class instance variables, the current UI state, and user preferences. They are most commonly used to persist data across user sessions and to share settings between application components.
Alternatively, Activities offer the onSaveInstanceState handler. It’s designed specifically to persist the UI state when the Activity becomes eligible for termination by a resource-hungry run time. The handler works like the Shared Preference mechanism. It offers a Bundle parameter that represents a key/value map of primitive types that can be used to save the Activity’s instance values. This Bundle is then made available as a parameter passed in to the onCreate and onRestoreInstanceState method handlers.
Creating and Saving Preferences
To create or modify a Shared Preference, call getSharedPreferences on the application Context, passing in the name of the Shared Preferences to change. Shared Preferences are shared across an application’s components but aren’t available to other applications. To modify a Shared Preference, use the SharedPreferences.Editor class. Get the Editor object by calling edit on the SharedPreferences object you want to change. To save edits, call commit on the Editor, as shown in the code snippet below.
public static final String MYPREFS = “mySharedPreferences”;
protected void savePreferences(){
// Create or retrieve the shared preference object.
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = getSharedPreferences(MYPREFS,
mode);
// Retrieve an editor to modify the shared preferences.
SharedPreferences.Editor editor = mySharedPreferences.edit();
// Store new primitive types in the shared preferences object.
editor.putBoolean(“isTrue”, true);
editor.putFloat(“lastFloat”, 1f);
editor.putInt(“wholeNumber”, 2);
editor.putLong(“aNumber”, 3l);
editor.putString(“textEntryValue”, “Not Empty”);
// Commit the changes.
editor.commit();
}
Retrieving Shared Preferences
Accessing saved Shared Preferences is also done with the getSharedPreferences method. Pass in the name of the Shared Preference you want to access, and use the type-safe get<type> methods to extract saved values. Each getter takes a key and a default value (used when no value is available for that key), as shown in the skeleton code below:
public void loadPreferences() {
// Get the stored preferences
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = getSharedPreferences(MYPREFS,
mode);
// Retrieve the saved values.
boolean isTrue = mySharedPreferences.getBoolean(“isTrue”, false);
float lastFloat = mySharedPreferences.getFloat(“lastFloat”, 0f);
int wholeNumber = mySharedPreferences.getInt(“wholeNumber”, 1);
long aNumber = mySharedPreferences.getLong(“aNumber”, 0);
String stringPreference;
stringPreference = mySharedPreferences.getString(“textEntryValue”,
“”);
Saving the Activity State
If you want to save Activity information that doesn’t need to be shared with other components, you can call Activity.getPreferences() without specifying a preferences name. Access to the Shared Preferences map returned is restricted to the calling Activity; each Activity supports a single unnamed SharedPreferences object. The following skeleton code shows how to use the Activity’s private Shared Preferences:
protected void saveActivityPreferences(){
// Create or retrieve the activity preferences object.
SharedPreferences activityPreferences =
getPreferences(Activity.MODE_PRIVATE);
// Retrieve an editor to modify the shared preferences.
SharedPreferences.Editor editor = activityPreferences.edit();
// Retrieve the View
TextView myTextView = (TextView)findViewById(R.id.myTextView);
// Store new primitive types in the shared preferences object.
editor.putString(“currentTextValue”,
myTextView.getText().toString());
// Commit changes.
editor.commit();
}
Saving and Restoring Instance State
To save Activity instance variables, Android offers a specialized alternative to Shared Preferences. By overriding an Activity’s onSaveInstanceState event handler, you can use its Bundle parameter to save instance values. Store values using the same get and put methods as shown for Shared Preferences, before passing the modifi ed Bundle into the superclass’s handler, as shown in the following code snippet:
private static final String TEXTVIEW_STATE_KEY = “TEXTVIEW_STATE_KEY”;
@Override
public void onSaveInstanceState(Bundle outState) {
// Retrieve the View
TextView myTextView = (TextView)findViewById(R.id.myTextView);
// Save its state
outState.putString(TEXTVIEW_STATE_KEY,
myTextView.getText().toString());
super.onSaveInstanceState(outState);
}
This handler will be triggered whenever an Activity completes its Active life cycle, but only when it’s not being explicitly finished. As a result, it’s used to ensure a consistent Activity state between active life cycles of a single user session. The saved Bundle is passed in to the onRestoreInstanceState and onCreate methods if the application is forced to restart during a session. The following snippet shows how to extract values from the Bundle and use them to update the Activity instance state:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView myTextView = (TextView)findViewById(R.id.myTextView);
String text = “”;
if (icicle != null && icicle.containsKey(TEXTVIEW_STATE_KEY))
text = icicle.getString(TEXTVIEW_STATE_KEY);
myTextView.setText(text);
}
Saving and Loading Files
It’s good practice to use Shared Preferences or a database to store your application data, but there are still times when you’ll want to use fi les directly rather than rely on Android’s managed mechanisms. As well as the standard Java I/O classes and methods, Android offers openFileInput and openFileOuput to simplify reading and writing streams from and to local fi les, as shown in the code snippet below:
String FILE_NAME = “tempfile.tmp”;
// Create a new output file stream that’s private to this application.
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
// Create a new file input stream.
FileInputStream fis = openFileInput(FILE_NAME);
These methods only support fi les in the current application folder; specifying path separators will cause an exception to be thrown. If the filename you specify when creating a FileOutputStream does not exist, Android will create it for you. The default behavior for existing fi les is to overwrite them; to append an existing fi le, specify the mode as Context.MODE_APPEND. By default, fi les created using the openFileOutput method are private to the calling application — a different application that tries to access these fi les will be denied access. The standard way to share a fi le between applications is to use a Content Provider. Alternatively, you can specify either Context.MODE_WORLD_READABLE or Context.MODE_WORLD_WRITEABLE when creating the output file to make them available in other applications, as shown in the following snippet:
String OUTPUT_FILE = “publicCopy.txt”;
FileOutputStream fos = openFileOutput(OUTPUT_FILE, Context.MODE_WORLD_WRITEABLE);
Including Static Files as Resources
If your application requires external fi le resources, you can include them in your distribution package by placing them in the res/raw folder of your project hierarchy. To access these Read Only fi le resources, call the openRawResource method from your application’s Resource object to receive an InputStream based on the specifi ed resource. Pass in the file name (without extension) as the variable name from the R.raw class, as shown in the skeleton code below:
Resources myResources = getResources();
InputStream myFile = myResources.openRawResource(R.raw.myfilename);
Adding raw files to your resources hierarchy is an excellent alternative for large, preexisting data sources (such as dictionaries) where it’s not desirable (or even possible) to convert them into an Android database.
Databases in Android
Android provides full relational database capabilities through the SQLite library, without imposing any additional limitations.
Using SQLite, you can create independent, relational databases for each application. Use them to store and manage complex, structured application data.
All Android databases are stored in the /data/data/<package_name>/databases folder on your device (or emulator). By default, all databases are private, accessible only by the application that create them. To share a database across applications, use Content Providers.
SQLite
SQLite is a relational database management system (RDBMS). It is well regarded, being:
❑ Open source
❑ Standards-compliant
❑ Lightweight
❑ Single-tier
It has been implemented as a compact C library that’s included as part of the Android software stack. By providing functionality through a library, rather than as a separate process, each database becomes an integrated part of the application that created it. This reduces external dependencies, minimizes latency, and simplifies transaction locking and synchronization.
For more comprehensive coverage of SQLite, including its particular strengths and limitations, check out the official site at www.sqlite.org/.
Cursors and Content Values
ContentValues objects are used to insert new rows into database tables (and Content Providers). Each Content Values object represents a single row, as a map of column names to values. Queries in Android are returned as Cursor objects. Rather than extracting and returning a copy of the result values, Cursors act as pointers to a subset of the underlying data. Cursors are a managed way of controlling your position (row) in the result set of a database query. The Cursor class includes several functions to navigate query results including, but not limited to, the following:
❑ moveToFirst Moves the cursor to the first row in the query result.
❑ moveToNext Moves the cursor to the next row.
❑ moveToPrevious Moves the cursor to the previous row.
❑ getCount Returns the number of rows in the result set.
❑ getColumnIndexOrThrow Returns an index for the column with the specified name (throwing an exception if no column exists with that name).
❑ getColumnName Returns the name of the specified column index.
❑ getColumnNames Returns a String array of all the column names in the current cursor.
❑ moveToPosition Moves the cursor to the specified row.
❑ getPosition Returns the current cursor position.
Android provides a mechanism to manage Cursor resources within your Activities. The startManagingCursor method integrates the Cursor’s lifetime into the parent Activity’s lifetime management. When you’ve finished with the Cursor, call stopManagingCursor to do just that.
Working with Android Databases
It’s good practice to create a helper class to simplify your database interactions. Consider creating a database adapter, which adds an abstraction layer that encapsulates database interactions. It should provide intuitive, strongly typed methods for adding, removing, and updating items. A database adapter should also handle queries and wrap creating, opening, and closing the database. It’s often also used as a convenient location from which to publish static database constants, including table names, column names, and column indexes. The following snippet shows the skeleton code for a standard database adapter class. It includes an extension of the SQLiteOpenHelper class, used to simplify opening, creating, and upgrading the database.
import android.content.Context;
import android.database.*;
import android.database.sqlite.*;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;
public class MyDBAdapter {
private static final String DATABASE_NAME = “myDatabase.db”;
private static final String DATABASE_TABLE = “mainTable”;
private static final int DATABASE_VERSION = 1;
// The index (key) column name for use in where clauses.
public static final String KEY_ID=”_id”;
// The name and column index of each column in your database.
public static final String KEY_NAME=”name”;
public static final int NAME_COLUMN = 1;
// TODO: Create public field for each column in your table.
// SQL Statement to create a new database.
private static final String DATABASE_CREATE = “create table “ +
DATABASE_TABLE + “ (“ + KEY_ID +
“ integer primary key autoincrement, “ +
KEY_NAME + “ text not null);”;
// Variable to hold the database instance
private SQLiteDatabase db;
// Context of the application using the database
private final Context context;
// Database open/upgrade helper
private myDbHelper dbHelper;
public MyDBAdapter(Context _context) {
context = _context;
dbHelper = new myDbHelper(context, DATABASE_NAME, null,
DATABASE_VERSION);
}
public MyDBAdapter open() throws SQLException {
db = dbHelper.getWritableDatabase();
return this;
}
public void close() {
db.close();
}
public long insertEntry(MyObject _myObject) {
ContentValues contentValues = new ContentValues();
// TODO fill in ContentValues to represent the new row
return db.insert(DATABASE_TABLE, null, contentValues);
}
public boolean removeEntry(long _rowIndex) {
return db.delete(DATABASE_TABLE, KEY_ID +
“=” + _rowIndex, null) > 0;
}
public Cursor getAllEntries () {
return db.query(DATABASE_TABLE, new String[] {KEY_ID, KEY_NAME},
null, null, null, null, null);
}
public MyObject getEntry(long _rowIndex) {
MyObject objectInstance = new MyObject();
// TODO Return a cursor to a row from the database and
// use the values to populate an instance of MyObject
return objectInstance;
}
public int updateEntry(long _rowIndex, MyObject _myObject) {
String where = KEY_ID + “=” + _rowIndex;
ContentValues contentValues = new ContentValues();
// TODO fill in the ContentValue based on the new object
return db.update(DATABASE_TABLE, contentValues, where, null);
}
private static class myDbHelper extends SQLiteOpenHelper {
public myDbHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
// Called when no database exists in
// disk and the helper class needs
// to create a new one.
@Override
public void onCreate(SQLiteDatabase _db) {
_db.execSQL(DATABASE_CREATE);
}
// Called when there is a database version mismatch meaning that
// the version of the database on disk needs to be upgraded to
// the current version.
@Override
public void onUpgrade(SQLiteDatabase _db, int _oldVersion,
int _newVersion) {
// Log the version upgrade.
Log.w(“TaskDBAdapter”, “Upgrading from version “ +
_oldVersion + “ to “ +
_newVersion +
“, which will destroy all old data”);
// Upgrade the existing database to conform to the new version.
// Multiple previous versions can be handled by comparing
// _oldVersion and _newVersion values.
// The simplest case is to drop the old table and create a
// new one.
_db.execSQL(“DROP TABLE IF EXISTS “ + DATABASE_TABLE);
// Create a new one.
onCreate(_db);
}
}
}
Using the SQLiteOpenHelper
SQLiteOpenHelper is an abstract class that wraps up the best practice pattern for creating, opening, and upgrading databases. By implementing and using an SQLiteOpenHelper, you hide the logic used to decide if a database needs to be created or upgraded before it’s opened.
Call getReadableDatabase or getWriteableDatabase to open and return a readable/writable instance of the database. A call to getWriteableDatabase can fail because of disk space or permission issues, so it’s good practice to provide fallback to the getReadableDatabase method as shown below:
dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
SQLiteDatabase db;
try {
db = dbHelper.getWritableDatabase();
}
catch (SQLiteException ex){
db = dbHelper.getReadableDatabase();
}
Opening and Creating Databases without the SQLiteHelper
You can create and open databases without using the SQLiteHelper class with the openOrCreateDatabase method on the application Context. Setting up a database is a two-step process. First, call openOrCreateDatabase to create the new database.
Then, call execSQL on the resulting database instance to run the SQL commands that will create your tables and their relationships. The general process is shown in the snippet below:
private static final String DATABASE_NAME = “myDatabase.db”;
private static final String DATABASE_TABLE = “mainTable”;
private static final String DATABASE_CREATE =
“create table “ + DATABASE_TABLE +
“ ( _id integer primary key autoincrement,” +
“column_one text not null);”;
SQLiteDatabase myDatabase;
private void createDatabase() {
myDatabase = openOrCreateDatabase(DATABASE_NAME,
Context.MODE_PRIVATE, null);
myDatabase.execSQL(DATABASE_CREATE);
}
Querying Your Database
All database queries are returned as a Cursor to a result set. This lets Android manage resources more efficiently by retrieving and releasing row and column values on demand. To execute a query on a database, use the query method on the database object, passing in:
❑ An optional Boolean that specifies if the result set should contain only unique values
❑ The name of the table to query
❑ A projection, as an array of Strings, that lists the columns to include in the result set
❑ A “where” clause that defines the rows to be returned. You can include wildcards that will be replaced by the values stored in the selection argument parameter.
❑ An array of selection argument strings that will replace the ?’s in the “where” clause
❑ A “group by” clause that defines how the resulting rows will be grouped
❑ A “having” filter that defines which row groups to include if you specified a “group by” clause
❑ A String that describes the order of the returned rows
❑ An optional String that defines a limit to the returned rows
The following skeleton code shows snippets for returning some, and all, of the rows in a particular table:
// Return all rows for columns one and three, no duplicates
String[] result_columns = new String[] {KEY_ID, KEY_COL1, KEY_COL3};
Cursor allRows = myDatabase.query(true, DATABASE_TABLE, result_columns,
null, null, null, null, null, null);
// Return all columns for rows where column 3 equals a set value
// and the rows are ordered by column 5.
String where = KEY_COL3 + “=” + requiredValue;
String order = KEY_COL5;
Cursor myResult = myDatabase.query(DATABASE_TABLE, null, where,
null, null, null, order);
Damn man, Very nice tutorial, Must have taken a long time to write. I want to be a Dev but i have no clue what code to start learning & where i can learn it at. I was told to learn Java programming & C+ to write apps. Is that right? I really want to create LWP. Can you tell me exactly what i have to learn to Dev apps? I looked all over the internet but couldn't really find anything in detail of what code you have to learn. Is this something you should go to school for?
well first of all java is very important. its practically the basic part of the whole being "android". so u must have some knowledge of java. if u dnt have then u will have some difficulity to learn ffrom this tuto coz i have basically written everything in java.
but if u have some practical programming knowledge then u will be able to figure it out urself.
but there are other SDK's like the google's appinventor. which will let u create apps without getting any knowledge of programming. i will be coming up with a tutorial on appinventor after this one. wait for it
thanks ...great work !!
Best Tutorial i have ever read in a forum...
Thanks a lot mate...
[Ultimate Guide][Part 7-2 released][5/09/2011]Android App Development - From Scratc
PART-VII-2
Introducing Content Providers
Content Providers are a generic interface mechanism that lets you share data between applications. By abstracting away the underlying data source, Content Providers let you decouple your application layer from the data layer, making your applications data-source agnostic.
Content Providers feature full permission control and are accessed using a simple URI model. Shared content can be queried for results as well as supporting write access. As a result, any application with the appropriate permissions can add, remove, and update data from any other applications — including some native Android databases.
Many of the native databases have been made available as Content Providers, accessible by third-party applications. This means that your applications can have access to the phone’s Contact Manager, media player, and other native database once they’ve been granted permission.
Using Content Providers
Access to Content Providers is handled by the ContentResolver class.
Introducing Content Resolvers
Each application Context has a single ContentResolver, accessible using the getContentResolver method, as shown in the following code snippet:
ContentResolver cr = getContentResolver();
Content Resolver includes several methods to transact and query Content Providers. You specify the provider to interact using a URI.
A Content Provider’s URI is defined by its authority as defi ned in its application manifest node. An authority URI is an arbitrary string, so most providers expose a CONTENT_URI property that includes its authority.
Content Providers usually expose two forms of URI, one for requests against all the data and another that specifies only a single row. The form for the latter appends /<rowID> to the standard CONTENT_URI.
Querying for Content
As in databases, query results are returned as Cursors over a result set. Content Provider queries take a very similar form to database queries. Using the query method on the ContentResolver object, pass in:
❑ The URI of the content provider data you want to query
❑ A projection that represents the columns you want to include in the result set
❑ A where clause that defi nes the rows to be returned. You can include ? wild cards that will be replaced by the values stored in the selection argument parameter.
❑ An array of selection argument strings that will replace the ?’s in the where clause
❑ A string that describes the order of the returned rows
The following skeleton code demonstrates the use of a Content Resolver to apply a query to a Content Provider:
// Return all rows
Cursor allRows = getContentResolver().query(MyProvider.CONTENT_URI,
null, null, null, null);
// Return all columns for rows where column 3 equals a set value
// and the rows are ordered by column 5.
String where = KEY_COL3 + “=” + requiredValue;
String order = KEY_COL5;
Cursor someRows = getContentResolver().query(MyProvider.CONTENT_URI,
null, where, null, order);
Adding, Updating, and Deleting Content
To perform transactions on Content Providers, use the delete, update, and insert methods on the ContentResolver object.
Inserts
The Content Resolver offers two methods for inserting new records into your Content Provider — insert and bulkInsert. Both methods accept the URI of the item type you’re adding; where the former takes a single new ContentValues object, the latter takes an array. The simple insert method will return a URI to the newly added record, while bulkInsert returns the number of successfully added items. The following code snippet shows how to use the insert and bulkInsert methods:
// Create a new row of values to insert.
ContentValues newValues = new ContentValues();
// Assign values for each row.
newValues.put(COLUMN_NAME, newValue);
[ ... Repeat for each column ... ]
Uri myRowUri = getContentResolver().insert(MyProvider.CONTENT_URI,
newValues);
// Create a new row of values to insert.
ContentValues[] valueArray = new ContentValues[5];
Deletes
To delete a single record using the Content Resolver, call delete, passing in the URI of the row you want to remove. Alternatively, you can specify a where clause to remove multiple rows. Both techniques are shown in the following snippet:
// Remove a specific row.
getContentResolver().delete(myRowUri, null, null);
// Remove the first five rows.
String where = “_id < 5”;
getContentResolver().delete(MyProvider.CONTENT_URI, where, null);
Updates
Updates to a Content Provider are handled using the update method on a Content Resolver. The update method takes the URI of the target Content Provider, a ContentValues object that maps column names to updated values, and a where clause that specifi es which rows to update. When executed, every matching row in the where clause will be updated using the values in the Content
Values passed in and will return the number of successful updates.
// Create a new row of values to insert.
ContentValues newValues = new ContentValues();
// Create a replacement map, specifying which columns you want to
// update, and what values to assign to each of them.
newValues.put(COLUMN_NAME, newValue);
// Apply to the first 5 rows.
String where = “_id < 5”;
getContentResolver().update(MyProvider.CONTENT_URI, newValues, where,
null);
Accessing Files in Content Providers
Content Providers represent files as fully qualified URIs rather than raw file data. To insert a file into a Content Provider, or access a saved file, use the Content Resolvers openOutputStream or openInputStream methods, respectively. The process for storing a file is shown in the following code snippet:
// Insert a new row into your provider, returning its unique URI.
Uri uri = getContentResolver().insert(MyProvider.CONTENT_URI,
newValues);
try {
// Open an output stream using the new row’s URI.
OutputStream outStream = getContentResolver().openOutputStream(uri);
// Compress your bitmap and save it into your provider.
sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
}
catch (FileNotFoundException e) { }
Native Android Content Providers
Android exposes many Content Providers that supply access to the native databases. You can use each of these Content Providers natively using the techniques described previously. Alternatively, the android.provider class includes convenience classes that simplify access to many of the most useful providers, including:
❑ Browser Use the browser Content Provider to read or modify bookmarks, browser history, or web searches.
❑ CallLog View or update the call history including both incoming and outgoing calls together with missed calls and call details, like caller ID and call durations.
❑ Contacts Use the Contacts provider to retrieve, modify, or store your contacts’ details.
❑ MediaStore The Media Store provides centralized, managed access to the multimedia on your device, including audio, video, and images. You can store your own multimedia within the Media Store and make it globally available.
❑ Settings You can access the device’s preferences using the Settings provider. Using it, you can view and modify Bluetooth settings, ring tones, and other device preferences.
Using the Media Store Provider
The Android Media Store provides a managed repository for audio, video, and image files. Whenever you add a new multimedia file to the Android filesystem, it should be added to the Media Store to expose it to other applications. The MediaStore class includes a number of convenience methods to simplify inserting files into the Media Store. For example, the following code snippet shows how to insert an image directly into the Media Store:
android.provider.MediaStore.Images.Media.insertImage(
getContentResolver(),
sourceBitmap,
“my_girlfriend_pic”,
“she is in my heaert!”);
Using the Contacts Provider
Access to the Contact Manager is particularly powerful on a communications device. Android does the right thing by exposing all the information available from the contacts database to any application granted the READ_CONTACTS permission. In the following example, an Activity gets a Cursor to every person in the contact database, creating an array of Strings that holds each contact’s name and phone number. To simplify extracting the data from the Cursor, Android supplies public static properties on the People
class that expose the column names.
// Get a cursor over every contact.
Cursor cursor = getContentResolver().query(People.CONTENT_URI,
null, null, null, null);
// Let the activity manage the cursor lifecycle.
startManagingCursor(cursor);
// Use the convenience properties to get the index of the columns
int nameIdx = cursor.getColumnIndexOrThrow(People.NAME);
int phoneIdx = cursor. getColumnIndexOrThrow(People.NUMBER);
String[] result = new String[cursor.getCount()];
if (cursor.moveToFirst())
do {
// Extract the name.
String name = cursor.getString(nameIdx);
// Extract the phone number.
String phone = cursor.getString(phoneIdx);
result[cursor.getPosition()] = name + “ (“ + phone + “)”;
} while(cursor.moveToNext());
To run this code snippet, you need to add the READ_CONTACTS permission to your application.
Creating a New Content Provider
Create a new Content Provider by extending the abstract ContentProvider class. Override the onCreate method to open or initialize the underlying data source you’re exposing with this new provider. The skeleton code for a new Content Provider is shown below:
import android.content.*;
import android.database.Cursor;
import android.net.Uri;
import android.database.SQLException;
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
}
}
You should also expose a public static CONTENT_URI variable that returns the full URI to this provider. Content URIs must be unique between providers, so it’s good practice to base the URI path on your package name. The general form for defi ning a Content Provider’s URI is
content://com.<CompanyName>.provider.<ApplicationName>/<DataPath>
For example:
content://com.sad.provider.myapp/items
Content URIs can represent either of two forms. The previous URI represents a request for all values of that type. Appending a trailing /<rownumber>, as shown below, represents a request for a single record.
content://com.sad.provider.myapp/items/5
It’s good form to support access to your provider using both these forms. The simplest way to do this is using a UriMatcher. Confi gure the UriMatcher to parse URIs to determine their form when the provider is being accessed through a Content Resolver. The following snippet shows the skeleton code for this pattern:
public class MyProvider extends ContentProvider {
private static final String myURI =
“content://com.paad.provider.myapp/items”;
public static final Uri CONTENT_URI = Uri.parse(myURI);
@Override
public boolean onCreate() {
}
// Create the constants used to differentiate between the different
// URI requests.
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;
private static final UriMatcher uriMatcher;
// Populate the UriMatcher object, where a URI ending in ‘items’ will
// correspond to a request for all items, and ‘items/[rowID]’
// represents a single row.
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(“com.paad.provider.myApp”, “items”, ALLROWS);
uriMatcher.addURI(“com.paad.provider.myApp”, “items/#”,
SINGLE_ROW);
}
}
Registering Your Provider
Once you have completed your Content Provider, it must be added to the application manifest. Use the authorities tag to specify its address, as shown in the following XML snippet:
<provider android:name=”MyProvider”
android:authorities=”com.sad.provider.myapp”/>
Thanks for all the information. I look forward to reading its entirety.
Sent from my ADR6400L using xda premium
What can i say.....This is awsome !!!
It's in my favorite straight know!!! Thank's a lot....Fantastic thread...
[Ultimate Guide][Part 8 released][20/09/2011]Android App Development - From Scratch
PART VIII
Using Location-Based Services
Location-based services (LBS) is an umbrella term used to describe the different technologies used to find the device’s current location. The two main LBS elements are:
❑ LocationManager Provides hooks to the location-based services.
❑ LocationProviders Each of which represents a different location-finding technology used to determine the device’s current location.
Selecting a Location Provider
Depending on the device, there may be several technologies that Android can use to determine the current location. Each technology, or Location Provider, will offer different capabilities including power consumption, monetary cost, accuracy, and the ability to determine altitude, speed, or heading information. To get an instance of a specifi c provider, call getProvider, passing in the name:
String providerName = LocationManager.GPS_PROVIDER;
LocationProvider gpsProvider;
gpsProvider = locationManager.getProvider(providerName);
Finding the Available Providers
The LocationManager class includes static string constants that return the provider name for the two most common Location Providers:
❑ LocationManager.GPS_PROVIDER
❑ LocationManager.NETWORK_PROVIDER
To get a list of names for all the providers available on the device, call getProviders, using a Boolean to indicate if you want all, or only the enabled, providers to be returned:
boolean enabledOnly = true;
List<String> providers = locationManager.getProviders(enabledOnly);
Finding Providers Based on Requirement Criteria
In most scenarios, it’s unlikely that you will want to explicitly choose the Location Provider to use. More commonly, you’ll specify the requirements that a provider must meet and let Android determine the best technology to use. Use the Criteria class to dictate the requirements of a provider in terms of accuracy (fine or coarse), power use (low, medium, high), cost, and the ability to return values for altitude, speed, and bearing. The following code creates Criteria that require coarse accuracy, low power consumption, and no need for altitude, bearing, or speed. The provider is permitted to have an associated cost.
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setSpeedRequired(false);
criteria.setCostAllowed(true);
Having defined the required Criteria, you can use getBestProvider to return the best matching Location Provider or getProviders to return all the possible matches. The following snippet demonstrates using getBestProvider to return the best provider for your criteria where the Boolean lets you restrict the result to a currently enabled provider:
String bestProvider = locationManager.getBestProvider(criteria, true);
If more than one Location Provider matches your criteria, the one with the greatest accuracy is returned. If no Location Providers meet your requirements, the criteria are loosened, in the following order, until a provider is found:
❑ Power use
❑ Accuracy
❑ Ability to return bearing, speed, and altitude
The criterion for allowing a device with monetary cost is never implicitly relaxed. If no provider is found, null is returned. To see a list of names for all the providers that match your criteria, you can use getProviders. It accepts Criteria and returns a fi ltered String list of all available Location Providers that match them. As with the getBestProvider call, if no matching providers are found, this call returns null.
List<String> matchingProviders = locationManager.getProviders(criteria,
false);
Finding Your Location
The purpose of location-based services is to find the physical location of the device. Access to the location-based services is handled using the Location Manager system Service. To access the Location Manager, request an instance of the LOCATION_SERVICE using the getSystemService method, as shown in the following snippet:
String serviceString = Context.LOCATION_SERVICE;
LocationManager locationManager;
locationManager = (LocationManager)getSystemService(serviceString);
Before you can use the Location Manager, you need to add one or more uses-permission tags to your manifest to support access to the LBS hardware. The following snippet shows the fine and coarse permissions. Of the default providers, the GPS provider requires fi ne permission, while the Network provider requires only coarse. An application that has been granted fine permission will have coarse permission granted implicitly.
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/>
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”/>
You can find the last location fix determined by a particular Location Provider using the getLastKnownLocation method, passing in the name of the Location Provider. The following example finds the last location fix taken by the GPS provider:
String provider = LocationManager.GPS_PROVIDER;
Location location = locationManager.getLastKnownLocation(provider);
Tracking Movement
Most location-sensitive applications will need to be reactive to user movement. Simply polling the Location Manager will not force it to get new updates from the Location Providers. Use the requestLocationUpdates method to get updates whenever the current location changes, using a LocationListener. Location Listeners also contain hooks for changes in a provider’s status and availability. The requestLocationUpdates method accepts either a specifi c Location Provider name or a set of Criteria to determine the provider to use. To optimize efficiency and minimize cost and power use, you can also specify the minimum time and
the minimum distance between location change updates. The following snippet shows the skeleton code for requesting regular updates based on a minimum time and distance.
String provider = LocationManager.GPS_PROVIDER;
int t = 5000; // milliseconds
int distance = 5; // meters
LocationListener myLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Update application based on new location.
}
public void onProviderDisabled(String provider){
// Update application if provider disabled.
}
public void onProviderEnabled(String provider){
// Update application if provider enabled.
}
public void onStatusChanged(String provider, int status,
Bundle extras){
// Update application if provider hardware status changed.
}
};
locationManager.requestLocationUpdates(provider, t, distance,
myLocationListener);
Using the Geocoder
Geocoding lets you translate between street addresses and longitude/latitude map coordinates. This can give you a recognizable context for the locations and coordinates used in location-based services and map-based Activities. The Geocoder class provides access to two geocoding functions:
❑ Forward Geocoding Finds the latitude and longitude of an address.
❑ Reverse Geocoding Finds the street address for a given latitude and longitude.
The results from these calls will be contextualized using a locale, where a locale is used to define your usual location and language. The following snippet shows how you set the locale when creating your Geocoder. If you don’t specify a locale, it will assume your device’s default.
Geocoder geocoder = new Geocoder(getApplicationContext(),
Locale.getDefault());
Both geocoding functions return a list of Address objects. Each list can contain several possible results, up to a limit you specify when making the call. Each Address object is populated with as much detail as the Geocoder was able to resolve. This can include the latitude, longitude, phone number, and increasingly granular address details from country to street and house number.
A note to all the readers and the app developers: using location based services or tracing movement will come directly under the privacy policy. An unauthorized tracking will lead to privacy breach. So if u want to use them, do so freely but mention why the location based services are needed, or if your app is totally based on location.
Thanks man! great tutorial! I was looking for exactly this great work
This is directly copied from the dev guide from android. Lame!
Sent from my Dream/Sapphire using XDA App

[Q] Help with Gallery App

I have found a Gallery App on GitHub (https://github.com/sevenler/Android_Gallery2D_Custom) that I have integrated into my own app.
Currently the gallery displays all images from my device. What I would like to have is that it ONLY display the images starting at the folder on my sdcard that I specify such as..
private static String targetPath = Environment.getExternalStorageDirectory().toString() + "/DCIM/_private/.nomedia";
I tried several different modifications but I am really struggling.
I was hoping someone could take a look at the source code for the Gallery App on GitHub and tell me how to change it so it does not scan my entire device.
Thanks in advance!!
-Steve
StEVO_M said:
I have found a Gallery App on GitHub (https://github.com/sevenler/Android_Gallery2D_Custom) that I have integrated into my own app.
Currently the gallery displays all images from my device. What I would like to have is that it ONLY display the images starting at the folder on my sdcard that I specify such as..
private static String targetPath = Environment.getExternalStorageDirectory().toString() + "/DCIM/_private/.nomedia";
I tried several different modifications but I am really struggling.
I was hoping someone could take a look at the source code for the Gallery App on GitHub and tell me how to change it so it does not scan my entire device.
Thanks in advance!!
-Steve
Click to expand...
Click to collapse
I had a look at the source and from my understanding the important class here is that DataManager class here. Especially the getTopSetPath() with the static final Strings and maybe the mapMediaItems() methods seem to be where you need to look at. You might have to play around a bit, but this is where I would start.
SimplicityApks said:
I had a look at the source and from my understanding the important class here is that DataManager class here. Especially the getTopSetPath() with the static final Strings and maybe the mapMediaItems() methods seem to be where you need to look at. You might have to play around a bit, but this is where I would start.
Click to expand...
Click to collapse
I have tried adding/changing the following
Added...
//Setting Folder Path
public static final String targetPath = Environment.getExternalStorageDirectory().toString() + "/DCIM/_private/.nomedia";
Changed...
// This is the path for the media set seen by the user at top level.
private static final String TOP_SET_PATH = targetPath;
private static final String TOP_IMAGE_SET_PATH = "/combo/{/mtp,/local/image,/picasa/image}";
private static final String TOP_VIDEO_SET_PATH = "/combo/{/local/video,/picasa/video}";
private static final String TOP_LOCAL_SET_PATH = "/local/all";
private static final String TOP_LOCAL_IMAGE_SET_PATH = "/local/image";
private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video";
I even tried setting all of the _PATH s to my targetPath
And I get a NullPointerException
12-13 11:07:47.881: E/AndroidRuntime(23029): Caused by: java.lang.NullPointerException
12-13 11:07:47.881: E/AndroidRuntime(23029): at com.myprivgallery.common.Utils.checkNotNull(Utils.java:48)
Which leads me to this in Utils.java
line 46 // Throws NullPointerException if the input is null.
line 47 public static <T> T checkNotNull(T object) {
line 48 if (object == null) throw new NullPointerException();
line 49 return object;
line 50 }
So if I am reading correctly, it is saying the path is empty. But I know that path works in other apps and it is not empty.
StEVO_M said:
I have tried adding/changing the following
Added...
//Setting Folder Path
public static final String targetPath = Environment.getExternalStorageDirectory().toString() + "/DCIM/_private/.nomedia";
Changed...
// This is the path for the media set seen by the user at top level.
private static final String TOP_SET_PATH = targetPath;
private static final String TOP_IMAGE_SET_PATH = "/combo/{/mtp,/local/image,/picasa/image}";
private static final String TOP_VIDEO_SET_PATH = "/combo/{/local/video,/picasa/video}";
private static final String TOP_LOCAL_SET_PATH = "/local/all";
private static final String TOP_LOCAL_IMAGE_SET_PATH = "/local/image";
private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video";
I even tried setting all of the _PATH s to my targetPath
And I get a NullPointerException
12-13 11:07:47.881: E/AndroidRuntime(23029): Caused by: java.lang.NullPointerException
12-13 11:07:47.881: E/AndroidRuntime(23029): at com.myprivgallery.common.Utils.checkNotNull(Utils.java:48)
Which leads me to this in Utils.java
line 46 // Throws NullPointerException if the input is null.
line 47 public static <T> T checkNotNull(T object) {
line 48 if (object == null) throw new NullPointerException();
line 49 return object;
line 50 }
So if I am reading correctly, it is saying the path is empty. But I know that path works in other apps and it is not empty.
Click to expand...
Click to collapse
I don't think you can call an Environment method when static class variables are Initialised make sure that your String is the correct path with Logs, I would probably make that an instance variable instead...
SimplicityApks said:
I don't think you can call an Environment method when static class variables are Initialised make sure that your String is the correct path with Logs, I would probably make that an instance variable instead...
Click to expand...
Click to collapse
It may kinda look like I know what I'm doing, but looks are deceiving.
I really have no clue what I am doing. I consider myself a hack. I can some what look at code and then make it do what I want, but to actually program something new... I will see if I can't figure that out. But I really do appreciate your help.
StEVO_M said:
It may kinda look like I know what I'm doing, but looks are deceiving.
I really have no clue what I am doing. I consider myself a hack. I can some what look at code and then make it do what I want, but to actually program something new... I will see if I can't figure that out. But I really do appreciate your help.
Click to expand...
Click to collapse
Well then you should probably get started on a simpler app you've built yourself rather than trying to understand this very complex Gallery app (which in my view is poorly commented and documented)...
I had a closer look at the source and it seems that we are on the right track because the _PATH s are used in the getTopSetPath method of the DataManager, and a search reveals that it is called by various activities and pickers. If you're using one of those in your app we should be right.
Now let's get back to the basics, when the app is launched, the default launcher activity is Gallery.java. During it's creation, either startDefaultPage, startGetContent or startViewAction is called, but all of them have the following call:
Code:
data.putString(AlbumSetPage.KEY_MEDIA_PATH, getDataManager().getTopSetPath(DataManager.INCLUDE_ALL)); // or some other variable
So this is the path where the images are loaded. Honestly, I have no idea what the
Code:
"/combo/{/mtp,/local/all,/picasa/all}"
is doing in the path variable, it has to do something with the Combo classes in the data folder, since the app is loading images from different dirs. You can now call your Environment call there and somehow pass back Strings instead of Paths, but it would be nicer if we could somehow figure out the these Paths are used here. An important class is the LocalPhotoSource.java. That is also where getImage from the DataManager is called. You could have a look at that class as well. Which path do you want the gallery to scan actually? The best solution would be to further experiment with the _PATH String, without calling Environment as well as debugging the whole app from the start, so you can get an idea of how everything is working.
SimplicityApks said:
Well then you should probably get started on a simpler app you've built yourself rather than trying to understand this very complex Gallery app (which in my view is poorly commented and documented)...
I had a closer look at the source and it seems that we are on the right track because the _PATH s are used in the getTopSetPath method of the DataManager, and a search reveals that it is called by various activities and pickers. If you're using one of those in your app we should be right.
Now let's get back to the basics, when the app is launched, the default launcher activity is Gallery.java. During it's creation, either startDefaultPage, startGetContent or startViewAction is called, but all of them have the following call:
Code:
data.putString(AlbumSetPage.KEY_MEDIA_PATH, getDataManager().getTopSetPath(DataManager.INCLUDE_ALL)); // or some other variable
So this is the path where the images are loaded. Honestly, I have no idea what the
Code:
"/combo/{/mtp,/local/all,/picasa/all}"
is doing in the path variable, it has to do something with the Combo classes in the data folder, since the app is loading images from different dirs. You can now call your Environment call there and somehow pass back Strings instead of Paths, but it would be nicer if we could somehow figure out the these Paths are used here. An important class is the LocalPhotoSource.java. That is also where getImage from the DataManager is called. You could have a look at that class as well. Which path do you want the gallery to scan actually? The best solution would be to further experiment with the _PATH String, without calling Environment as well as debugging the whole app from the start, so you can get an idea of how everything is working.
Click to expand...
Click to collapse
I tried Changing the following in LocalAlbumSet.java
public class LocalAlbumSet extends MediaSet {
public static final Path PATH_ALL = Path.fromString("/DCIM/_private/.nomedia/all");
public static final Path PATH_IMAGE = Path.fromString("/DCIM/_private/.nomedia/image");
public static final Path PATH_VIDEO = Path.fromString("/DCIM/_private/.nomedia/video");
and Changing all of the TOP_..._Path s to
// This is the path for the media set seen by the user at top level.
private static final String TOP_SET_PATH = "/local/all";
private static final String TOP_IMAGE_SET_PATH = "/local/image";
private static final String TOP_VIDEO_SET_PATH = "/local/video";
private static final String TOP_LOCAL_SET_PATH = "/local/all";
private static final String TOP_LOCAL_IMAGE_SET_PATH = "/local/image";
private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video";
I now see Random Images from /DCIM/_private/.nomedia but not all of them, which is really confusing. But at least it's a start.
StEVO_M said:
I have found a Gallery App on GitHub (https://github.com/sevenler/Android_Gallery2D_Custom) that I have integrated into my own app.
Currently the gallery displays all images from my device. What I would like to have is that it ONLY display the images starting at the folder on my sdcard that I specify such as..
private static String targetPath = Environment.getExternalStorageDirectory().toString() + "/DCIM/_private/.nomedia";
I tried several different modifications but I am really struggling.
I was hoping someone could take a look at the source code for the Gallery App on GitHub and tell me how to change it so it does not scan my entire device.
Thanks in advance!!
-Steve
Click to expand...
Click to collapse
Android gallery will not scan any folder having a .nomedia file in it.
EatHeat said:
Android gallery will not scan any folder having a .nomedia file in it.
Click to expand...
Click to collapse
So I am guessing there is no way to Force your own instance to scan it????
StEVO_M said:
So I am guessing there is no way to Force your own instance to scan it????
Click to expand...
Click to collapse
Delete the .nomedia file.
Lol. Not an option if I want to hide my pic.
Sent from my HTCONE using Tapatalk
Not sure what you are trying to achieve, if you want to scan it then why would you want to hide it?
If you want to add an option to allow it to scan or not, you could backup the .nomedia to another directory and delete it for the time being.
Just an idea, can't say for sure. Didn't try it ever.
I have created an app to hide pictures. Currently I have a very basic gallery. I was trying integrate this gallery because it has a lot more option such as editing and adding filters and such.
Sent from my HTCONE using Tapatalk
StEVO_M said:
I have created an app to hide pictures. Currently I have a very basic gallery. I was trying integrate this gallery because it has a lot more option such as editing and adding filters and such.
Click to expand...
Click to collapse
Well why do you need to? Make your app fulfill only one purpose, so let it hide the pictures (I guess by moving a .nomedia file into the folder right?) and then just create a shortcut in your app to the gallery app so the user can still use their preferred one... Makes more sense to me and is way less work.
StEVO_M said:
I have created an app to hide pictures. Currently I have a very basic gallery. I was trying integrate this gallery because it has a lot more option such as editing and adding filters and such.
Sent from my HTCONE using Tapatalk
Click to expand...
Click to collapse
A far more simple and logical way would be to create a hidden folder on the SD card with a .nomedia file in it. Images/videos that are to be hidden can be moved to that location. This would keep it hidden from the gallery and can only be accessed through your private gallery.
For unhiding media, just move them back to their original folders.
Let me try to explain further... As I said, I have created an app already that does the Basic things.
My Application looks like a standard App, a "To Do List", but if you press and hold an Icon on the main screen it prompts you for a Password (These passwords are setup on first use).
Now... Depending on which password you enter, it will either take you to the hidden Gallery OR if you enter a different password it will take you to a Hidden To Do List. This is so anyone that may stumble upon your app, you can show them that it's has a hidden area of the app. This way they have no clue you really have a Hidden Gallery.
If you enter the Hidden Gallery password then it would of course, take you to the hidden gallery.
The Current Features are...
Import from Main Gallery
Pinch to Zoom
Swipe
Share
Restore back to Main Gallery
Delete
Quick Escape ------ "Quick Escape" can be activate 2 ways. If you shake the phone while in the Hidden Gallery, the image will change to an image of your choosing or the default one that is set during install. OR..... A less obvious "Quick Escape" ... While in the Hidden Gallery, press the volume up or down and the image will switch. Either way, once it is in that mode, you can not press back. the only way out it to press the home button. This is designed so if someone grabs the phone out of your hand they will only see they Quick Escape" image and pressing back will not take them back to the gallery.
My app also appears in the standard Share menu, so if you are in your Main Gallery and want to hide a picture, you can share it to my app and it will then hide the picture.
I want to add More features to my Gallery, such as Filters, Editing (crop, rotate...), Frames... All the same things that you can do in the Main Gallery. The only thing I really don't want or need is a Camera function. I can't see someone taking the time to open my app and then get to the Hidden Gallery and then taking a picture, just so they can hide an image. Most would just take the picture as they always would and then "Share" it to my app.
I hope this explains Why I want to use the other Gallery rather than reinventing the wheel that already has all of these features built in. If I can not get that to work, maybe I can just pull the Features out and use those.
Anyone who wants to actually See the app in action, PM me and I will send you a DropBox link to install. FYI, It requires 4.0 or higher.

Categories

Resources