Android Essentials


Chris Haseman Android Essentials BOOKS FOR PROFESSIONALS BY PROFESSIONALS® Android Essentials Dear Reader, This book covers the essential information required to build an Android applica- tion. It will help any developer, amateur, professional, or dabbler who is interested in developing for Android. Over the course of the book, I cover the essentials you’ll need to get started with your own innovative application. I took on this project because, after five years of working in the mobile software industry, it has become clear that we need help. With mobile devices becoming more ubiquitous, powerful, and, indeed, essential, fresh blood is necessary to overcome the stagnation that has plagued our business. As an industry, we need to graduate from making cookie-cutter ringtones, wallpaper, and e-mail applications. I hope, once you’ve finished this book, that you’ll be in a position to start that killer mobile product you’ve told all your friends you’re going to make. In Android Essentials, I cover the details of installing and using the Android SDK, making and rendering user interface tools, harnessing location tracking and Google Maps, and putting everything together with a little glue from XML parsers and net- working clients. These tools and tricks should be enough to get you past your empty editor, the daunting blank canvas of the developer world, and onto the path of your own innovative mobile app. Android potentially represents our best hope for a break from the constricted world of mobile development. Use it to make something amaz- ing. I can’t wait to see what you create. Regards, Chris Haseman Lead software engineer at Gravity Mobile Haseman Android Essentials Apress’s firstPress series is your source for understanding cutting-edge technology. Short, highly focused, and written by experts, Apress’s firstPress books save you time and effort. They contain the information you could get based on intensive research yourself or if you were to attend a conference every other week—if only you had the time. They cover the concepts and techniques that will keep you ahead of the technology curve. Apress’s firstPress books are real books, in your choice of electronic or print-on-demand format, with no rough edges even when the technology itself is still rough. You can’t afford to be without them. User level: Beginner–Intermediate www.apress.com SOURCE CODE ONLINE 116 PAGES Available as a PDF Electronic Book or Print On Demand RELATED TITLES About firstPress Apress's firstPress series is your source for understanding cutting-edge technology. Short, highly focused, and written by experts, Apress's firstPress books save you time and effort. They contain the information you could get based on intensive research yourself or if you were to attend a conference every other week—if only you had the time. They cover the concepts and techniques that will keep you ahead of the technology curve. Apress's firstPress books are real books, in your choice of electronic or print-on-demand format, with no rough edges even when the technology itself is still rough. You can't afford to be without them. Android Essentials Dear Reader, This book covers the essential information required to build an Android application. It will help any developer, amateur, professional, or dabbler who is interested in developing for Android. Over the course of the book, I cover the essentials you’ll need to get started with your own innovative application. I took on this project because, after five years of working in the mobile software industry, it has become clear that we need help. With mobile devices becoming more ubiquitous, powerful, and, indeed, essential, fresh blood is necessary to overcome the stagnation that has plagued our business. As an industry, we need to graduate from making cookie-cutter ringtones, wallpaper, and e-mail applications. I hope, once you’ve finished this book, that you’ll be in a position to start that killer mobile product you’ve told all your friends you’re going to make. In Android Essentials, I cover the details of installing and using the Android SDK, making and rendering user interface tools, harnessing location tracking and Google Maps, and putting everything together with a little glue from XML parsers and networking clients. These tools and tricks should be enough to get you past your empty editor, the daunting blank canvas of the developer world, and onto the path of your own innovative mobile app. Android potentially represents our best hope for a break from the constricted world of mobile development. Use it to make something amazing. I can’t wait to see what you create. Regards, Chris Haseman Lead software engineer at Gravity Mobile Android Essentials i Contents Chapter 1: Introduction...............................................................1 What You Need to Know to Start............................................................1 How to Best Use This Book ....................................................................2 Getting Started.........................................................................................2 Installing Eclipse ........................................................................................... 3 Getting the Android SDK.............................................................................. 3 Installing the Eclipse Plug-In ........................................................................ 4 The Android Project................................................................................5 Running, Debugging, and Causing General Mayhem..........................8 Chapter 2: The Application ..........................................................9 Getting Active ..........................................................................................9 Getting Splashy ........................................................................................... 10 Creating the Intent Receiver.................................................................19 Setting It Up ................................................................................................ 19 Seeing the Intent Receiver in Action........................................................... 23 Triggering the Activity................................................................................ 26 Who Do You Want to Humiliate Today? .............................................29 Nervous with the Service ............................................................................ 29 Zen and the Art of Getting Even ................................................................. 33 Moving Data in Android.......................................................................34 Shameless Self-Promotion .......................................................................... 36 Adding Evil Corporate URLS with a Content Resolver ............................. 38 ii Android Essentials Part of This Balanced Breakfast ..........................................................39 Chapter 3: User Interface ..........................................................41 Easy and Fast, the XML Layout...........................................................42 Laying Out................................................................................................... 42 Scrolling, Text Entry, Buttons, and All the Simple Things in Life ........... 51 Waking Up the Widgets.........................................................................55 Widgets in Java ........................................................................................... 59 Getting Under the Hood .............................................................................. 59 Custom UI Rendering with the Canvas................................................67 Customizing the View................................................................................. 67 Creating the Game Loop ............................................................................. 69 Bringing It All Together.............................................................................. 74 Using the User Interface.......................................................................74 Chapter 4: Location, Location, Location ......................................75 Where Am I? .........................................................................................75 Building the LocationManager Object........................................................ 76 Look Up, Wave, the Satellites Are Watching… ......................................... 77 Google Maps..........................................................................................80 A Metric Ton of Map Objects..................................................................... 80 Moving the Map .......................................................................................... 82 Taking Stock................................................................................................ 84 It’s a Bird, It’s a Plane...Nope, It’s Bad Photoshoping ............................... 84 Wrapping Up ............................................................................................... 87 Chapter 5: Taking Android Out for a Walk...................................89 Loading a List from the Web ................................................................89 First Things…First? .................................................................................... 90 Getting the Network in Gear ....................................................................... 92 Android Essentials iii Putting the Data in Its Place ........................................................................ 94 Making a List and Checking It….........................................................95 The Setup: Embracing the List.................................................................... 95 Adding the Adapter ..................................................................................... 96 Stuffing Data into the Adapter .................................................................... 98 At Last, Adding the Data............................................................................. 99 Selection… ................................................................................................ 100 The Next Step ......................................................................................102 Dressing Up the Menu............................................................................... 102 Looking Back ......................................................................................106 Chapter 6: Tying on a Bow ...................................................... 107 The Making of an Application ...........................................................107 Looks Aren’t Everything, Except, of Course, When They Are................ 107 Location Isn’t Too Important, Except When You Need Pizza at 4 a.m.... 108 Taking Off Android’s Leash and Letting It Romp Around the Internet... 109 Overall....................................................................................................... 109 Other Sources of Information ............................................................109 Getting Help .............................................................................................. 110 It’s Time to Stop Reading and Start Helping.....................................110 Copyright ............................................................................... 112 iv Android Essentials Android Essentials Chris Haseman So, you want to be an Android developer? Good, you’ve come to the right place. Whether you’re a dabbler, professional, hobbyist, or coding junkie, I hope you’ve picked up my book out of a desire to learn about the somewhat schizophrenic world that is mobile development. Specifically, I hope you’re interested in picking up the gauntlet thrown down by the Open Handset Alliance’s Android team. I took the opportunity to write this book because it’s clear that Android has the potential to open up the mobile space in ways that keep us jaded mobile programmers up late at night. Volumes could be written about the errors of past platforms and the shortsightedness of an industry whose greatest achievement, at least here in America, is selling several million ringtones. You and your peers can bring about a long-needed change to the industry, perhaps bringing a much- needed breath of fresh air to an environment that, over the years, appears to have stagnated. You’ll have to forgive my enthusiasm; it has been a long wait. Android Essentials 1 Chapter 1: Introduction Before you start, we’ll need to have a quick discussion about where you’re beginning. What You Need to Know to Start It is a natural question you may be asking yourself: is this book for you? Yes, obviously, it is because you’re reading it. However, I will make a few assumptions about your abilities: ƒ You understand Java; you can write it, read it, and grok it. Because this book is about the Android platform and not the language, I will be writing large amounts of Java code inline and assuming you can follow along. If your Java skills are rusty, I recommend checking out Apress’s wealth of Java information on its site (http://java.apress.com/). ƒ Some familiarity with other mobile platforms will help you. As you move through the book, I’ll make comparisons between Android and other mobile software development kits (SDKs). You don’t need to be a professional mobile developer to follow along by any means. ƒ You have superior hacker skills. OK, not really, but if you’re comfortable rolling up your proverbial sleeves and digging into the heart of a problem, you should feel right at home with this book. ƒ I will assume you have exactly zero experience developing for Android. If you’ve already mastered the basics, you may want to skip the first chapter and focus on the more advanced topics that follow. That wasn’t a big list, but it contained a few things that will help you follow the book with your sanity intact. Ideally, I want this book to be useful and valuable to anyone interested in developing applications for Android. Hobbyists will find a foundation here for their dream application. Mobile game developers will find the nuts and 2 Android Essentials bolts of graphical output and user input. Multimedia and application developers will find all the tricks, tips, and core functionality they need to put together the next major killer app. If you’re a business-oriented person looking into porting your existing applications to Android, you’ll find invaluable information on how to accomplish exactly that. In short, this book has a lot to offer you no matter your desired outcome, experience, time, or interest. How to Best Use This Book The simple answer is to read it, but this may mean different things to different people. If you’re new to mobile development and Android, it would be best for you to treat this book as a tutorial. Follow along, chapter by chapter, until you have all the basics you need to get working. If you’re a more experienced Java and mobile programmer but are inexperienced with Android, you might want to treat this book as more of a reference manual after going through the first chapter to get a feel for things. Throughout this work, I will primarily use real-world examples as a means to get you comfortable with Android. This book may not have a huge appeal to those who are already established veteran Android developers. As I said before, I will start from a place that assumes no prior experience with this SDK. This book will start simple: a splash screen, a main menu, and some simple multimedia. I’ll then get into the more advanced concepts of Bluetooth, location-based services, background applications, and the other exciting features Android has to offer. Enough talking, it’s time to start. Getting Started It begins with installing the SDK. On a personal note, I’m doing all my development on Mac OS X with Eclipse. All screenshots, IDE information, Android Essentials 3 tips, and tricks will be geared toward the Eclipse IDE. It seems the Android developers had the open source IDE Eclipse in mind, because they’ve released a plug-in that eases setup and debugging. For the sake of simplicity, I use Eclipse and the Open Handset Alliance’s Android. I do not endorse this setup over any other. I will, however, take a little bit of time to walk through downloading and configuring Eclipse to integrate it with Android. If you’re already up and running on the SDK, skip to “The Android Project” section. Additionally, you can find a much more in-depth install guide on Google’s SDK installation page (http://code.google. com/android/intro/installing.html#installingplugin). Installing Eclipse Again, because Eclipse will be used in the book’s examples, download the full version at http://www.eclipse.org/downloads/. Be sure to get the Java EE version. It includes frameworks for a few editors that the full Google Eclipse plug-in will use. Install Eclipse; the default configurations should work just fine. Note With Windows and with the Mac, it’s a good idea to keep your files and SDK installation out of folders that contain spaces. Many tools such as Ant, among others, can be confused by spaces in folder names. Getting the Android SDK You can find the Android SDK on Google’s website at http://code.google.com/android/download.html. Grab it, download it somewhere handy, and then unpack it. I’ve put mine in my Development folder at /Developer/AndroidSDK. You can just as easily put the ZIP file anywhere on your filesystem. Just remember where you’ve stashed it, because you’ll need to tell Eclipse 4 Android Essentials where it is later. It’s also a good idea, if you’re a Windows or Linux user, to add the location of the Android tools to your Path variable. Installing the Eclipse Plug-In I like graphical user interfaces (GUIs), provided they have hotkeys like the Eclipse IDE, when working with Android. To get more than basic functionally out of the process, you’ll need to download the Android Developer Tools. To install it from within Eclipse, follow the directions outlined by Google at http://code.google.com/android/intro/ installing.html#installingplugin. If you’ve already installed the older version of the SDK and the Eclipse plug-in, I recommend you go back and update it to M5-RC15 (or the latest version) now using the previously mentioned links. Enough has changed between the older version and the latest that the changing details could be confusing. If you correctly follow the directions but get an error when trying to install the Android editors, go back and install the full version of Java EE Eclipse. The basic Java SDK doesn’t include all the correct packages used by the Android plug-in. Don’t forget to point the Android plug-in to where you unpacked your copy of the SDK. It’ll be in Windows/Preferences/Android on the Android tab. Create a new project by selecting File h New h Android Project. Give the project and activity a pithy name of your choosing. You’ll also have to insert into your source package name at least one dot (.), such as apress. book.sample or crazy.flyingmonkey.application. Eclipse gives a fairly unhelpful error message if you forget to give it more than one name separated by dots. I can personally testify that this can be fairly frustrating if your brain is fried and you’re, say, trying to get a book done on a deadline. Android Essentials 5 The Android Project So, you’re now the proud owner of your own basic Android application. If this isn’t the case, because you skipped the previous section, create a new project right now. New projects, by default, have an implementation of “Hello, World.” Books and articles that start by explaining “Hello, World” aren’t particularly useful in my humble opinion. Consequently, I’m not going to take your time breaking down the functionality of the most basic Android application. What is worth taking some time to look into, however, are the contents, layout, and files of your new project. Let me briefly explain how each file and directory contributes to a functioning Android project (see Table 1-1). Table 1-1. Files in a Basic Android Project FILE NAME PURPOSE YourActivity.java File for your default launch activity; more on this to follow. R.java File containing an ID for all asset constants. Android Library/ Folder containing all Android’s SDK files. assets/ Multimedia and other miscellaneous required files. res/ Base directory for resources used by the UI. res/drawable Directory for image files to be rendered by the UI layer. res/layout All XML-style view layout files are stored here. Again, more on this later. Continued 6 Android Essentials Table 1-1. continued FILE NAME PURPOSE res/values Location for string’s and configuration files. AndroidManifest.xml File that describes your application to the outside operating system. Obviously, you’ve moved well beyond the days of Hello_World.c. As you move forward, I’ll mention these files in passing. If you have trouble following later in the book, refer to this table. I’ll take a few minutes to break down a few essentials. Most central, and probably most confusing, among the various files is the Android manifest. This is the link between your application and the outside world. In this file, you will register intents for activities. (I’ll get into intents and activities, as well as how they work, in the following chapter.) So that you have the groundwork for further development, Code Listing 1-1 shows what the Android manifest looks like when you create your first project. Code Listing 1-1. Android Manifest.xml Android Essentials 7 After the xml/declaration comes the tag with the schema URL. One of its properties is the name of your source package. In the case of this example, it is apress.book.sample. Next up is the application declaration and location of the application icon. The @drawable notation indicates that the item can be found in the res/drawable folder. The application, at this point, consists of a single activity: SampleApp. The activity name is hard-coded, but its label, or what is displayed to the user within the Android application menu, is defined in the res/values/strings.xml file. The @string notation pulls the value from app_name within the aforementioned XML file. Now you come to the intent filter. I will go into intent filters at length in the next chapter, but for now, you need to know that android.intent. action.MAIN and android.intent.category.LAUNCHER are predefined strings that tell Android which activity to fire up as your application is started. That’s all there is to the manifest file in its most basic form. For space reasons, I will try to avoid writing out the entire manifest file again. If you need context for elements that are being inserted later, dog-ear this page and return to it later. Note Get in the habit of moving as many strings, resources, and screen layouts into the res folder as much as humanly possible. It may cost you a little more time to work from these files up front, but once you start porting your application to the different languages, screen sizes, and function sets, your prep time will be paid back with interest. I’m sure this is something you read in computer manuals constantly, but it bears repeating. A little bit of planning and overhead up front can save you weeks of porting and debugging on the back side of a project. This is particularly important when developing for mobile devices, because each application can have many separate build environments. 8 Android Essentials Next up on the important file list is R.java. This file is where reference identification numbers live for all your resources, both graphical and drawable. Adding a new graphic to res/drawable should result in a corresponding identifier in the R.java class. References can then be made to these IDs rather than the items on the filesystem. This allows you to swap out strings for localization, screen size, and other errata that are guaranteed to change between devices. You’ll get into the other files as you add functionality to your repertoire. Let’s get your application up and running. Running, Debugging, and Causing General Mayhem Running and debugging are, thankfully, extremely straightforward with Android. From within Eclipse, pull down the Run menu and select…wait for it…Run. This launches the Android emulator. Tip Once you’ve started the emulator for the first time, you don’t need to quit or close it down. The latest version of the SDK ships with an emulator that can take minutes to “boot up.” While the emulator is running, each subsequent Run or Debug execution will build and deploy your latest code to the emulator. The emulator will accept the changes and automatically launch your new project. This is the opposite of BREW or J2ME, which must relaunch after each change in your source base. Once the Android emulator is up and running, you should see the “Hello World, YourApplicationName” message. Again, “Hello, World” as a programming example is, for lack of a better term, emphatically lame, so I’ll skip it and move on to a more practical example: a simple splash screen. Along the way, you should come to understand activities, intents, intent filters, services, and content providers. Android Essentials 9 Chapter 2: The Application An application in Android is defined by the contents of its manifest. Each Android application declares all its activities, entry points, communication layers, permissions, and intents through AndroidManifest.xml. Four basic building blocks, when combined, comprise a rich Android application: ƒ Activity: The most basic building block of an Android application ƒ Intent receiver: A reactive object launched to handle a specific task ƒ Service: A background process with no user interface ƒ Content provider: A basic superclass framework for handling and storing data In this chapter, I’ll break down each specific piece with a concrete functional example. First up is the activity, the core building block of a stand-alone Android application. Getting Active All Android mobile applications, at least in the traditional sense, will revolve around an activity. If you’ve had any experience with other mobile platforms, Android’s activity is quite similar to BREW’s applet or Java ME’s midlet. There are, however, a few very important differences. Android vs. Java ME vs. BREW A BREW application will, in the vast majority of all cases, consist of a single applet. That applet communicates with the rest of the handset through receiving and sending events. You can think of a Java ME application, on the other hand, as an extension of the Midlet class. The midlet has functions for starting, stopping, pausing, key handling, and performing any other interaction between the handset and application. A Java ME application usually consists of a single midlet. 10 Android Essentials Android applications can have any number of activities registered with the handset through the AndroidManifest.xml file. Android’s multiactivity architecture is probably the major difference between developing for Android and developing for other handset SDKs. This single fact makes it much easier to write modular, compartmentalized code. In BREW and Java ME, a developer will implement most functionality within the confines of the midlet or the applet. In Android, you can write an activity, content handler, intent receiver, or service to handle nearly anything. Once you’ve written an activity to edit a text file, you can refer to this activity in all future applications you write by sending and receiving intent actions. This isn’t to say that such architecture isn’t possible within BREW or Java ME. It just has to be done at the Java, C, or C++ level or, in Brew, with cumbersome extensions instead of being built smoothly into the application framework. Functionality Just like the midlet, an activity uses a series of functions to interact with the outside world. At its base, your activity must override the method onCreate. Other functions you’ll want to override are onStop, onPause, onResume, and onKeyDown. These few functions are what will let you tie your activity into the Android handset at large. By default, new Android applications created within Eclipse will implement a “Hello, World” application. I’ll show you how to go from this basic application to a fully functional splash screen. Getting Splashy You can download the packaged version of this splash screen example from the downloads section of www.apress.com if you want to use it as a starting point for your own Android application or you want to follow along in a more passive fashion. In this example, because it is your first, I will go through it in a series of small steps. I’ll break down exactly what Android Essentials 11 needs to be written, from adding a new image resource to modifying XML layout files. Down the road, examples will not parse out into such minute detail. This should be the only chapter that will read like a tutorial for beginners. Adding the Image Resource First you’ll need a sample splash screen image. The “socially awkward” splash screen I’ve included is not going to win any awards, but it is a good poke at the rash of social networking applications that seem to keep cropping up in the mobile space right now. To add this new resource, I’ve placed menu_background.jpg inside res/drawable. Make sure a new ID is added to R.java. It should look something like this: public static final int menu_background=0x7f020001; This is now your means of loading and drawing the image from within your code. You’ll return to this concept in the next chapter on user interaction. Creating an XML Layout File Now that you have an image resource, you can add it to your XML layout file. These files are kept in res/layout/, and you should currently have one called main.xml. Add a new XML file called splash.xml, and copy the contents of the main.xml file into it. Next, modify the file by removing the tag and add an tag that looks like the following: Using Android’s XML layout objects is simple and straightforward. As I mentioned, files in the /res directories can be referenced with the @ 12 Android Essentials symbol as shown earlier, for example, android:src="@drawable/menu_background". Further, layout_width and layout_height dictate the size of the image view. Look to make sure your new layout file has been added to R.java. It should appear as follows: public static final int splash=0x7f030001; Drawing the Splash Screen Now that your splash screen is defined, it’s time to activate and paint it. Your existing Android activity is already drawing main.xml, so you’ll shift to your new splash layout. To make the switch, change this code: setContentView(R.layout.main); in the method onCreate to this code: setContentView(R.layout.splash); Run the application, and watch your newly created splash screen come to life. If you’ve gotten errors thus far, check to make sure your names for everything match up. If the image isn’t drawing, make sure it’s been correctly placed in the res/drawable folder and that splash.xml refers to the correct name and file. Timing Is Almost Everything The splash screen is now rendering, but splash screens alone make for boring applications, so you’ll need to move on to the main menu. You’ll use a simple inline-defined thread to accomplish the timing. There are a few constants initialized before the thread that I’ve included. For the sake of completeness, I’ve included the entirety of the onCreate method. Code Listing 2-1 shows what mine looks like with the timing thread in place. Code Listing 2-1. Timing the Splash Screen long m_dwSplashTime = 3000; boolean m_bPaused = false; Android Essentials 13 boolean m_bSplashActive = true; public void onCreate(Bundle icicle) { super.onCreate(icicle); //Draw the splash screen setContentView(R.layout.splash); //Very simple timer thread Thread splashTimer = new Thread() { public void run() { try { //Wait loop long ms = 0; while(m_bSplashActive && ms < m_dwSplashTime) { sleep(100); //Advance the timer only if we're running. if(!m_bPaused) ms += 100; } //Advance to the next screen. startActivity(new Intent( "com.google.app.splashy.CLEARSPLASH")); } catch(Exception e) { Log.e("Splash", e.toString()); } finally { finish(); } } }; splashTimer.start(); } 14 Android Essentials At long last, you’re getting into some Java code. This simple thread will run until the time counter exceeds m_dwSplashTime. Although there are other methods for implementing a timer, I like this one for two reasons: ƒ It can be paused. The timer will advance only if the m_bPaused flag is false. As you’ll see in a minute, it’s easy to suspend the timer if your activity’s onPause method is called. This is not always a requirement for a splash screen, but it is important for other timing-based operations. ƒ Moving to the next screen is as simple as flipping the m_bSplashActive flag to false. Advancing to the next screen, if you implement it in this fashion, does not require you to make the move and then cancel a more traditional timer. With this code in place, you should see the splash screen for as long as you set m_dwSplashTime in milliseconds. When that time is up or the user interrupts the splash screen with a key press, startActivity will be called (I’ll explain this shortly). This function will move the user to what will become the main menu in the next chapter. finish closes down the splash activity so the user does not return to it when they press Back from the main menu. You’ll need to implement an activity that accepts the CLEARSPLASH intent action. In the meantime, let’s review a few other important activity methods you’ll want to override. Pause, Resume, Rinse, Repeat Pausing the splash timer when your activity is suspended by an incoming call, SMS message, or other interruption is as easy as the following: protected void onPause() { super.onPause(); m_bPaused = true; } Android Essentials 15 As with most of these overridden methods, you’ll need to invoke the superclass before doing anything else. If you review the timer thread, you’ll see that the ms counter responsible for keeping track of time passed doesn’t advance if m_bPaused is true. At this point, I’m sure you can guess what onResume will look like: protected void onResume() { super.onResume(); m_bPaused = false; } No surprises here. When your application is resumed, the timer thread will resume adding time to the ms counter. Basic Key Handling Key handling within the activity is handled by overriding the onKeyDown method. We’ll use this function to allow a user to cancel your fledgling splash screen. As you can see in the timer thread at the start of this section, you’ve set up an escape clause in the timer loop by the name of m_bSplashActive. To escape, you’ll just override the onKeyDown method so that it flips the splash flag to false. Here’s the code you’ll need to add: public boolean onKeyDown(int keyCode, KeyEvent event) { //if we get any key, clear the splash screen super.onKeyDown(keyCode, event); m_bSplashActive = false; return true; } Now, when the user hits any key, the screen will be advanced on the next trip through the timer loop. 16 Android Essentials ADDITIONAL EXERCISES If you want to make this a fully functional splash screen, you’ll need to add two elements. This gives me a great excuse to assign you some homework. Try to add two bits of functionality: ▪ Allow the user to skip the splash screen only when clicking the OK or center key. ▪ Make the Back key exit the application rather than putting it in the background. As a hint, you’ll want to look up the key-code mapping in the Android SDK documentation to accomplish these tasks. Clear Intent There’s one more thing you’ll need to do before you’re done with the splash screen. I’m sure you’re wondering about that startActivity method call earlier. This means it’s time to talk briefly, in this limited context, about the intent. An intent is an object that functions as a communication event between two or more activities, content handlers, intent receivers, or services. In this case, you will call startActivity with the intent com.google.app.splashy.CLEARSPLASH. When startActivity is called, Android searches all its manifests for the node that has registered for the aforementioned CLEARSPLASH intent action. It just so happens that you’ll add your own activity called MainMenu that will register for just such an intent. To create what will become the main menu activity, add a new class to your existing source package called MainMenu. Next, make sure it extends the Activity class, implements onCreate, and calls setContentView on R.layout.main. At this point, you’ll want to open AndroidManifest.xml and add a new activity element to it. After the Android Essentials 17 closing tag of the splash screen, you should insert the following: Define the activity’s name as .MainMenu. This will tell Android which Java class to load and run. Register, within the intent filter tag, for the com.apress.splash.CLEARSPLASH intent action. In reality, the name of the intent could be beef.funkporium.swizzle, and as long as the name is consistent between the startActivity call and the snippet of the Android manifest listed earlier, all the right things should continue to happen. Running It Running your application at this point should result, if you’ve paid attention thus far, in your splash screen being drawn for a few seconds, followed by your new main menu activity taking focus. It should also be impossible to get back to the splash screen once your application has advanced to the main menu. If you’re having trouble, make sure your new main menu is listed in the manifest and in R.java. Also, make sure you’re drawing the correct layout file within your new intent. The Life Cycle of an Activity The life cycle of an activity is covered extensively in the Google documentation. However, if I’m reviewing the nuts and bolts of what makes an activity, I cannot pass by this important information. At this point, with your splash screen, you should be ready to roll. 18 Android Essentials For explanation’s sake, I’ve also added the following functions to the splash screen activity: protected void onStop() { super.onStop(); } protected void onDestroy() { super.onDestroy(); } If you place breakpoints in all the functions within your new splash activity and run it in debug mode, you’ll see the breakpoints hit in the following order: 1. onCreate 2. onStart 3. onResume 4. At this point, your activity is now running. Three seconds in, the timer thread will reach its end and call startActivity with the splash clearing intent. Next, it will call finish, which tells Android to shut down the splash screen activity. 5. onPause 6. onStop 7. onDestroy This is the general life cycle of an activity from start to finish. You can find a much more comprehensive exposé on the life and times of an Android activity in Google’s documentation at http://code.google.com/ android/reference/android/app/Activity.html. You’ll even find a spiffy graph. In essence, the handset uses a combination of the previous functions to alert you to the major events that can occur in your application: starting, closing, suspending, and resuming. Activities, as I’ve Android Essentials 19 covered before, are going to be the core building block of any traditional application; they give you control over the screen and the ability to receive user inputs. You’ll get more into user interaction in later chapters. Thus Far So far I’ve explored how activities are integrated within the phone, how they are started and stopped, and how they communicate on a basic level. I’ve demonstrated how to display a simple XML view screen and how to switch between two actives both in reaction to a key event and at the end of a set amount of time. In the short-term, you’ll need to learn more about how Android uses intents, intent receivers, and intent filters to communicate. To do this, you’ll need another sample application. Creating the Intent Receiver An intent receiver is one of the few things in Android that does exactly what its name implies. Its role is to hang around waiting for registered intent actions, Android’s version of BREW-style notifications. I’ll use a somewhat less production-worthy application to demonstrate one of the trickier tasks of an intent receiver: receiving and reacting to incoming text messages. Setting It Up Let me paint you a picture. You’ve returned to your desk one afternoon to discover that you’ve fallen victim to a Hello Kitty attack. Your office desk is covered, from carpet to ceiling, with cute pink images of the most annoying icon known to humankind. You know who’s done it, and it’s payback time. You also know that your VP of engineering hates a particular song with a fiery passion matched by nothing else. That song is “La Bamba.” You decide to get even by rigging your co-worker’s Android phone with a sleeper application. I’ll show you how to make an Android application that will respond to a specific SMS message by playing an 20 Android Essentials audio file for maximum humiliation effect. This will make your newfound enemy the subject of intense anger from your VP. At the same time, you’ll want your victim to know he’s been had…and give him a chance to shut the sound off. This prank application requires an intent receiver, an activity, a service, and the means for all three to communicate. Imagine your co-worker’s surprise when his phone starts spouting the very song your VP of engineering hates most in the world. What Practical Use Could This Possibly Have? This is an excellent question. Although on the surface this may not seem to be the most practical of applications, I’m sure, with a little imagination, you can come up with a variety of important realistic uses for this little prank application, from push e-mail notifications to interphone application communication. Besides, things have been far too serious thus far. You’ll move forward in four stages. In each phase, you’ll learn more about intent receivers, services, and the interactions between all these application pieces: ƒ Being notified on arrival of an SMS ƒ Opening the contents of an SMS and looking for a specific payload ƒ Starting an activity when the SMS arrives and being aware that the startup has occurred at the behest of the intent receiver ƒ Starting a new service that will play an audio file Using Intent Receivers Before you get into building the intent receiver, you need to take a quick moment to learn why you would use one. Intent receivers have little to no memory footprint, linkage, or overhead. Where an activity has to load all of its heavy imported classes on startup, an intent receiver has none of these obligations. Because new intents of a certain type could arrive with crushing frequency (network status updates, for example), a lightweight Android Essentials 21 object must take the first pass at parsing the data. If it is an appropriate time to awaken a larger UI process or hefty background service, the intent receiver should take such an action. Tip Intent receivers can be started and closed frequently (depending on what they listen for); try to make them lightweight and use as few libraries as you can get away with. Your users will not be happy if their phone is slowed to a crawl because you’ve inserted too much overhead into the processing of any particular event. Building the Intent Receiver First things first—you’ll need to create a new project for the little prank application. In the source directory, create a new class that will become the new intent receiver. At first pass, it should look like this: public class PrankSMSReceiver extends IntentReceiver { public void onReceiveIntent (Context context, Intent intent) { return; } } Now that you have the class set up, you’ll need to tell Android you’d like to receive SMS events. You do this by modifying the AndroidManifest.xml file to give you permission and to register for the RECEIVE_SMS intent action. Permissions Carriers, users, and even developers may not want to give Android applications free reign to run through the privileged layers of their handsets and networks. Consequently, Google has introduced a notion of 22 Android Essentials permissions in Android (something all developers with previous mobile experience should recognize). To be able to receive SMS messages, you’ll need to notify the handset that you’re allowed to receive them. Because permissions, in Android, are declared for all elements within a particular manifest, you’ll add the permissions line after the declaration tag (Code Listing 2-2). The sample app will be called PrankApp. Its main activity is called PrankActivity. No namespace prizes should be given to this writer for originality. Code Listing 2-2. Adding Permissions to Receive SMS Messages Without the permissions flag, Android will not launch your application when it receives an SMS. There are other permissions I’ll need to cover as you move along. In the meantime, you can find a list of all permissions in Android’s documentation at http://code.google.com/android/ reference/android/Manifest.permission.html. Send Me SMS Too! Now that you have permission to interact with the SMS layer of the handset, you have to tell the handset what to do when a new text message arrives. To accomplish this, you must open the AndroidManifest.xml file as you’ve done before. You’ll add a new intent receiver alongside the existing activity. Code Listing 2-3 shows what to insert (I’ve left the tag in place for reference). Android Essentials 23 Code Listing 2-3. Registering the New Intent Receiver for Incoming SMSs That’s all you should need to receive an intent notification each time the handset gets an incoming SMS. Seeing the Intent Receiver in Action This is a little more difficult to pull off than it sounds. A process must be running for the DDMS (the debugger application) to attach to it. But in most cases you don’t want the application to run except when a new event triggers it. The solution is a little bit of a shell game with the Eclipse IDE. This may get a little more complicated as you advance to the later steps. Place a breakpoint inside your onReceiveIntent method. Start debugging the application, and let the emulator sit on the “Hello World, PrankActivity” screen. In Eclipse, switch views to the DDMS. You can do this by pressing Command+F8 a few times or by selecting the menu Window h Open Perspective h DDMS. Figure 2-1 shows what it looks like. 24 Android Essentials Figure 2-1. The DDMS perspective Along the left window, on the Devices tab, you should now see your application highlighted with a little green bug next to it. This is the DDMS telling you that the debugger is attached to your PrankApp process. A little further down is Emulator Control tab. This is where you’ll send the SMS message. Enter any phone number first, select SMS, type a test message, and hit Send. Android Essentials 25 You should, if you’ve set up your manifest correctly, see Eclipse switch back to the Debug perspective and halt on your newly set breakpoint. Note If nothing is happening, first make sure you’ve set the permissions correctly. If they’re not correct, you should see a failed permissions message go by on the bottom of the DDMS screen on the LogCat tab. Also, make sure your application is already running and has the green debugging icon next to it on the Devices tab. If all else fails, compare your project with the sample one I’ve included with this book. If you’ve correctly followed along so far, your intent’s onReceiveIntent function will now be called each time an SMS message is sent to the handset. Next, you’ll have to figure out how to retrieve the contents of an SMS message. What’s in an SMS? Sadly, to date, Android’s documentation on receiving and filtering SMS messages is confusing at best. I suspect it’s not a feature that’s high on the list of things to tell developers about. Although I disagree with these priorities, it gives me a chance to fill the gap. Here’s what the methodonReceiveIntent looks like now that you’re listening for new SMS messages: public void onReceiveIntent (Context context, Intent intent) { SmsMessage msg[] = Telephony.Sms.Intents.getMessagesFromIntent(intent); for(int i = 0; i < msg.length; i++) { String msgTxt = msg[i].getMessageBody(); if (msgTxt.equals("0xBADCAT0_Fire_The_Missiles!")) { 26 Android Essentials //Start the pranking here } } return; } You’ll also need to import two libraries to make this work: import android.telephony.gsm.SmsMessage; import android.provider.Telephony; Buried in the Telephony library is the call getMessageFromIntent, which will return an array of SMS messages. All that’s left is pulling the payload out of the SMS messages in question. The special code you’ll be looking for to trigger your prank activity is the text “0xBADCAT0_Fire_The_Missiles!” It must be a combination suitably unique so that it will not be triggered by accident. You wouldn’t want your prank to misfire and alert the victim too early. Note In Android, features that are not documented well are very likely not finished. Because the documentation for receiving SMS messages is nearly nonexistent, you should expect some changes in how SMSs are processed. More than likely, the overall method should be similar, but it’s safe to assume that some of the details will change before the SDK reaches its final version. This example is more about learning how to use an intent receiver than it is about the particulars of text message communication. Triggering the Activity It’s important to remember that the life cycle of an intent receiver lasts only as long as the method call onReceiveIntent. Once out of that function, Android is free to kill the process running your application. Any asynchronous functionality will die a messy death if started. If you want to Android Essentials 27 do anything beyond simple processing within the method, you’ll need to start a service or an activity. Since you want to both play music and alert your victim that they’ve been had, you’ll need to start up an activity. You accomplish this as follows: if (msgTxt.equals("0xBADCAT0_Fire_The_Missiles!")) { //Start the Activity Intent startActivity = new Intent(); startActivity.setLaunchFlags(Intent.NEW_TASK_LAUNCH); startActivity.setAction("com.apress.START_THE_MUSIC"); context.startActivity(startActivity); } The only unrecognizable code you’ll see here is the addition of NEW_TASK_LAUNCH in setLaunchFlags. You’ll need to do this any time you want to send out an intent action that will start a new activity. Additionally, just as you did in the splash screen example application, you’ll have to add a new action to the intent filter of your activity. The process should look familiar: Now, if you’ve correctly added the earlier code in place, when you send a SMS message from the DDMS perspective, you should see your application come to the foreground and the “Hello World, PrankActivity” text display proudly on the screen. Rigging the Activity There is one last piece you’ll need to add before getting into the more dastardly music playback service: rigging up the activity to correctly 28 Android Essentials respond to the action sent by your SMS intent receiver. If the application is started normally, you’ll want to immediately close it down. Again, you can’t have the victim of your prank launching it from the menus and clueing in to your plan too early. To do this, you’ll have to retrieve the launching intent and call the getAction method to figure out under what case it has been launched. The PrankActivity’s onCreate method should now look like Code Listing 2-4. Code Listing 2-4. Launching on a Specific Intent Action public void onCreate(Bundle icicle) { super.onCreate(icicle); Intent i = getIntent(); String action = i.getAction(); if (action != null && action.equals("com.apress.START_THE_MUSIC")) { setContentView(R.layout.pranked); //We'll need to start the music service here } else finish(); } First you’ll get the intent that launched your activity. With the intent in hand, you can retrieve the calling action with the getAction method. This will return a string containing the launching event that you’ll check against your known music action listed in the previous XML. If the launch event for your activity comes from normal means (from the menu or from starting the debugger), the action string will be null. If this is the case, you’ll want to shut down the activity immediately using the finish method. Android Essentials 29 Note The onCreate method is called only when your application is started for the first time. If you’ve launched your application and then exited it (using the Back key), the application will still be running in the background. If, at that point, you send the SMS message, your activity will come back to the foreground, but its onStart method will be called but not onCreate. Feel free to move the functionality of your own sample (if you’re following along) to the onStart method. Who Do You Want to Humiliate Today? Although it might be a little overkill, you’ll use one of Android’s Service objects to handle music playback. I’m going to do it this way for two reasons: ƒ It’s a great chance to demonstrate the use of a service in a simple, uncomplicated environment. ƒ Potentially, it will let you start up the music without a visible application presence, making your prank application that much more dastardly. Nervous with the Service Why would you want to use a service? Essentially, it’s meant to be an object that runs as a separated process from the user interface. It’s perfect for cases when a developer wants functionality (be it network or multimedia related) to be able to run independently. Examples include audio playback, background web transactions, and evil prank applications. Although services allow multiple applications to bind (to open a communications channel), with them you’ll be using it as a simple background process. Again, services have a wealth of uses beyond the simple one you’re putting them to here. 30 Android Essentials Creating a Service Add a new class to your source package. I’ve called my PrankService again (no points for originality). At its most basic level, a service must override the onBind method. To get a service class to compile, it must look, at least a little bit, like Code Listing 2-5. Code Listing 2-5. A Stripped-Down Service public class PrankService extends Service { public IBinder onBind(Intent intent) { return null; } } For this example, you won’t be using the onBind method of the service interaction. You’ll simply be starting and stopping the service from within your main activity. To do this, you’ll have to override two more methods within the Service class: ƒ onStart(int startId, Bundle arguments) ƒ onDestroy() When onStart is called, you’ll begin playing your sample media file. When the service is destroyed, you’ll explicitly stop it. This is not necessarily required, but you’ll spell it out a little more later for the sake of explanation. Starting the Service Starting a new service should look similar to starting an activity. Since you’ve already been exposed to this a few times before, I’ll just drop in the code and let you sort it out yourself. Android Essentials 31 Recall the onCreate method within PrankActivity listed earlier. Simply replace the comment “We’ll need to start the music service here” with the following line of code: startService(new Intent ("com.apress.START_AUDIO_SERVICE"), null); Again, this should look familiar. The only difference between starting an activity and starting a service (aside from the different method call) is the ability to pass a bundle (essentially a map or hash table) of parameters along with the intent. That bundle will be passed to the onCreate method of the service. Starting the Music Like BREW and Java ME, Android has made media playback (at least on a simple play/stop basis) very simple and easy to use. When your service’s onStart function is called, you’ll load and play a test audio file from the /res/raw directory. The first order of business is to copy a sample audio file into /res/raw (if you don’t have this directory, go ahead and create it). Next, drop your humiliating, and copy-write respecting, audio file into the raw folder. If you’re using Eclipse, you should add a corresponding element to R.raw. In your case, it’s R.raw.test. Now that you have a music file to reference, you can add the procedural calls to your PrankService as follows: public void onStart(int startId, Bundle arguments) { MediaPlayer p; super.onStart(startId, arguments); player = MediaPlayer.create(this, R.raw.test); player.start(); } Remember, onStart is an overridden method, so you’ll have to call the superclass version of the same function first, or Android will get cranky with you. At that point, you’ll just have the MediaPlayer static class 32 Android Essentials create a new media player object. Because a service is a child of the Context class, you’ll pass a pointer to your current context and the static variable representing your test media. At this point, you can call play, and you’re off to the races. Playback should continue in the background with this service until the stopService method is called by your main activity. When stopService is called, the following method will be called: public void onDestroy() { super.onDestroy(); player.stop(); } An Act of Mercy Since you’re being a nice prankster, you’ll give your victim a way out. As you’ve seen before, the activity is triggered by the intent receiver at which point the activity starts the service. As you’ve just seen, the service is responsible for playing the noise that will so irritate your fictional VP of engineering. Again, because you’re being merciful in your execution of payback, you’ll have to build in a way for the victim to turn off the music. Adding the following method in your PrankActivity can accomplish your act of grace: public boolean onKeyDown(int keyCode, KeyEvent event) { stopService(new Intent( "com.apress.START_AUDIO_SERVICE")); finish(); return true; } Manifestation Here’s what the manifestation looks like: Android Essentials 33 Zen and the Art of Getting Even Through the use of a devious little prank application, you’ve explored how intents, intent receivers, services, and activities work together in an advanced, mostly background, application. I’ll now go over, step by step, what you did and how you accomplished it. Getting It Done You did the following: 1. You used an intent receiver with the right permissions and a system-level SMS intent to arrange for your PrankSMSReciever object to be instantiated each time an SMS arrives on the phone. If your intent receiver detected a very specific SMS payload, it would respond by sending an intent that would start your activity. 2. This activity, named PrankActivity, would listen for the specific intent action sent by the PrankSMSReceiver. When it received that precise intent action, your activity would display a “gotcha” message to the victim. At the same time, the activity would send out an intent meant to start up a service. If, at any point the victim/user pressed a key on the phone, the application would exit, and the music service would be terminated. 3. The service class, called PrankService, listening for the PrankActivity’s intent, would start and begin to play an obnoxious, predefined audio file. It would continue to play until it was told to stop by the PrankActivity’s call to the method stopService. 34 Android Essentials Note This sample application does not deal with the handset’s native SMS application. Because all intent receivers are notified of an incoming intent, your application will be competing for user attention with Android’s SMS inbox application. In production, this may require a substantial timer and perhaps a trigger text payload, which is a little more subtle than “0xBADCAT0_Fire_The_Missiles!” FURTHER DEVIOUSNESS Here are a few ways you can explore and extend the prank application on your own: ▪ Get more evil by taking the activity out of the loop. Launch the PrankService directly from the intent receiver. Do not give the victim a way of shutting off the music. ▪ Add a different text payload to stop the music. This exercise would be an excellent one to combine with the previous one. ▪ Customize your “get even” message. Create a prefix that triggers the service and a payload, which is displayed by the main app activity. Pass this payload from the intent receiver to the activity using a payload within the intent. Taunting, sometimes, needs fine- tuning. These are just a few ways you can better learn the pieces of Android while at the same time making life miserable for those around you. Moving Data in Android Finally, to round your knowledge about Android’s application building blocks, you need to focus on the content resolver. Android does not give the SDK particular access to the phones filesystem, as Brew does. Nor does it offer a RecordStore, as does Java ME. Your primary method for passing data between your activities, intent receivers, and services is going to be through the ContentResolver superclass. Though you can store data through files, preferences, and other databases, content resolvers can take Android Essentials 35 many forms, and Android ships with a few important content resolvers built in. Here’s a list, at time of publication, of the major Android content resolvers you’ll probably want to interact with on a regular basis: ƒ Browser ƒ Bookmarks ƒ Search history ƒ Phone calls ƒ Call log ƒ Recent calls ƒ Contacts ƒ System settings ƒ Hardware settings (Bluetooth, networking settings) ƒ Software settings Android’s documentation gives an excellent walk-through of using the contacts content resolver here: http://code.google.com/android/ devel/data/contentproviders.html#usingacp. Quickly, I’ll walk you through adding a bookmark to the phone browser’s bookmark list. First, you’ll want to search the current list of bookmarks to see whether your link is in place. Second, you’ll add your bookmark if it isn’t there. Note It is possible to create your own content providers as a way to wrap Android’s SQLite implementation for universal access. You’ll get into how to do this in later chapters. For now you’re just going to handle the “client” side of this content resolver interaction. 36 Android Essentials Android uses a custom implementation of SQLite to store information locally. If you’re not familiar with the basics of SQL, now might be a good time to brush up. I’m going to assume, for the sake of expediency, that you understand basic SQL searching commands. If you need to brush up, Apress has an excellent resource at http://apress.com/book/ catalog?category=145. Shameless Self-Promotion Let’s say in the “About” section of your application that you want to have a button that adds your commercial software page to the user’s web bookmarks. You want to make sure it isn’t added twice if your user has clicked the button again by accident. For the sake of this simple demonstration, you’ll trigger this event in your sample application when the user presses a key. Note On an amusing note, if you need proof, as a developer, that Android is still not quite fully baked, you need look no further than the documentation for android.content.ContentResolver under the method getDataFilePath, which states “DO NOT USE THIS FUNCTION!! Someone added this, and they shouldn't have. You do not have direct access to files inside of a content provider. Don't touch this. Go away.” It’s good to know that even the technical writers for Android’s documentation have a sense of humor. Fetching the User’s Bookmarks It should be obvious, at least at this point, that a developer could do some fairly nefarious things with access to a user’s bookmarks. It’s not clear, at this point, what Android will do to keep this sort of thing from happening. I suppose it’s up to the carriers to lock down or monitor this behavior. In any case, you’ll use a call to the method managedQuery, which will return a list of the user’s bookmarks: Android Essentials 37 Cursor bookmarks = android.provider.Browser.getAllBookmarks (getContentResolver()); int urlColumn = bookmarks.getColumnIndex( android.provider.Browser.BookmarkColumns.URL); Cursor results; String[] proj = new String[] { android.provider.BaseColumns._ID, android.provider.Browser.BookmarkColumns.URL, android.provider.Browser.BookmarkColumns.TITLE }; results = managedQuery(android.provider.Browser.BOOKMARKS_URI, proj, null, android.provider.Browser.BookmarkColumns.URL + " ASC"); I’ll now break down what’s happening. You’ll first get the column index of the bookmark URL. Again, because Android provides access to most of its internal data in a SQL format, you should get used to referring to your saved information in a database-centric way. Next you’ll set up the cursor, an object similar to a Java ME RecordStore enumerator and set up your projection string array. Because you’re interested only in the columns containing the URL, you can keep it very simple. The method call managedQuery is the call that will return your data. You’ll pass in the URI string for the bookmarks store, hand it your simple projection array, leave the where section empty, and tell it to sort the URLs in descending order. Searching the Results Searching through the results is as simple as iterating through the Curser object and pulling out a string from the URL column id you retrieved earlier: Cursor results = android.provider.Browser.getAllBookmarks (getContentResolver()); int urlColumn = 38 Android Essentials results.getColumnIndex (android.provider.Browser.BookmarkColumns.URL); results.first(); do { //url is a method param //containing what we're looking for if(results.getString(urlColumn).equals(url)) return false; } while(results.next()); You could do more based on the contents of the URL, but for now you’ll just look for your www.apress.com link. Obviously, if this code were run with the aforementioned Apress URL, you won’t find it. Since the user wants to add your corporate URL in your fictional “About” section, you’ll have to oblige them. Adding Evil Corporate URLS with a Content Resolver Maybe they aren’t evil, but you’ll add them anyway. Since Apress is probably one of the least evil of companies out there (not that I’m biased, mind you), you’ll let them get away with it, just this once. Here’s the fancy ContentReceiver way of adding the bookmark records: ContentValues inputValues = new ContentValues(); inputValues.put (android.provider.Browser.BookmarkColumns.BOOKMARK, "1"); inputValues.put (android.provider.Browser.BookmarkColumns.URL, "http://www.apress.com/"); inputValues.put (android.provider.Browser.BookmarkColumns.TITLE, "Apress, the not so evil company"); ContentResolver cr = getContentResolver(); Uri uri = cr.insert (android.provider.Browser.BOOKMARKS_URI, inputValues); Android Essentials 39 As with most SDKs, there’s more than one way to accomplish the same task. Earlier, you had the more complex way of adding a bookmark. This approach is useful because it gives you a reference for how to add elements through a ContentResolver that doesn’t have helper functions. Now, here’s the easy way: android.provider.Browser.saveBookmark(this, "Apress", url); The helper function will activate a dialog box asking the user to confirm the bookmark add. This is probably the most user-friendly way of adding bookmarks—unless you want to control what the dialog box looks like. Part of This Balanced Breakfast In the past three examples you explored all of Android’s major building blocks. You started by looking at a functional splash screen. This let you explore the essentials of starting, maintaining, and moving through the Activity object. It let you get your foot though the door about intents and interprocess/object communication. Using and passing intents between activities, services, content handlers, and intent receivers is probably one of the most important things that sets Android apart from the other mobile environments. With activities and communication basics under your belt, you moved on to the service and intent receiver. To use these two building blocks, you cooked up a devious prank application that would both force you to use all three pieces (activity, service, and intent receiver) and make all three communicate with each other. Almost as a side note, you explored what it takes to be notified when an SMS message arrives on the phone. Last, you explored how to retrieve and write to a content resolver that is native to the device: the browser’s bookmark database. Proof that things do not always go as planned, Android rebuffed your attempts at adding a new bookmark through the traditional content resolver method, which forced 40 Android Essentials you to fall back on a helper function designed to do the same thing. Now that I’ve covered the basics, it’s time to let the handset user weigh in a little more. Android Essentials 41 Chapter 3: User Interface In Soviet Google, the Interface Renders You In the scrum of mobile UI development architectures, Android’s rises to the top. If you have some mobile experience, you’ll find it to be a happy union of Java ME’s Canvas/Screen object and the BREW widget hierarchy, with some XML layout tools to boot. Each activity, as it’s launched from within your application, is placed on a screen stack. Android is already configured to handle closing down the top activity and activating the one under it when you ask or when the user presses Back. This setup allows you to think of every activity as the base for a single screen. Each activity may contain different views and view groups in a hierarchical tree. You can visualize this tree with the view groups and layout objects as the trunk and branches (because view group objects can be cast into views) and with the views or widgets as the leaves. A single view, in its most basic format, is a drawable rectangle. A view group, in its most basic format, is an object containing one or more views. This object hierarchy allows you to lay out complex user interfaces without having to go through the error- prone process of calculating view rectangles and widget overlap maps. If, on the other hand, that sort of thing is your bag, Android will stay out of the way and let you render in the style of Java ME’s hand-drawn game canvas. In this chapter, you’ll start with basic XML-based screen layouts and move toward the more complex custom canvas drawing. For the sake of this book, I’ll break down and discuss views as three major food groups: ƒ XML-defined widgets/views and view groups: Good for basic information display and menus ƒ Android native views: TextViews, LayoutGroups, ScrollBars, and text entry ƒ Custom views: The game programmer’s best friend 42 Android Essentials You’ll start with a sample login screen, move into manipulating and laying out widgets and views in code, and finally render an interactive animation with a custom view. Easy and Fast, the XML Layout Getting started with XML layouts might seem simple at first, but it’s going to get complicated really quickly. You’ll start with the layouts and work your way down to the individual elements. Laying Out Most XML screens will be wrapped in a layout object. Layout objects come in many different flavors, each of which you’ll look at really quickly and then check out with a simple example in the following sections. Caution At compile time, these XML layout files are parsed and packed by Android into a tight binary format. This saves monstrous amounts of parsing time on startup. However, it means that the XML files cannot be changed by your code during runtime. More specifically, you may be able to change these XML files during execution, but it will do absolutely nothing to the layout of your application. Additionally, you have to pay a small performance price for inflating a view or view group from XML. Your mileage may vary depending on CPU load and UI complexity. Android Essentials 43 LinearLayout All elements are arranged in a descending column from top to bottom or left to right. Each element can have gravity and weight properties that denote how they dynamically grow and shrink to fill space. Elements arrange themselves in a row or column notation based on the android:orientation parameter. For example (see Figure 3-1): 44 Android Essentials Figure 3-1. Linear layout example Android Essentials 45 RelativeLayout Each child element is laid out in relation to other child elements. Relationships can be established so that children will start themselves where a previous child ends. Children can relate only to elements that are listed before them. So, build your dependency from the beginning of the XML file to the end. Note that IDs are required so that widgets can reference each other. For example (see Figure 3-2): 46 Android Essentials Figure 3-2. Relative layout example Android Essentials 47 AbsoluteLayout Each child must be given a specific location within the bounds of the parent layout object. The AbsoluteLayout object is probably the easiest to build and visualize but the hardest to migrate to a new device or screen size. For example (see Figure 3-3): 48 Android Essentials Figure 3-3. Absolute layout example Android Essentials 49 TableLayout TableLayout is a layout object that allows you to specify table rows. Android tries to arrange each of the child elements into the correct row and columns. For example (see Figure 3-4): 50 Android Essentials Figure 3-4. Table layout example Android Essentials 51 These are the major layout objects you’ll be using as you go forward. Each example has a few simple TextView elements to demonstrate how the layout shakes down for each layout type and a screen capture depicting how each XML file will render. You can find more thorough examples in the Android documentation at http://code.google.com/android/ samples/ApiDemos/src/com/google/android/samples/view/. Note If you’re new to mobile development, when deciding how to lay out your application, you must repeat one motto in your head: “porting, porting, porting.” Ideally, one layout could be set up that would work for all possible devices. In reality, this never works. If you plan on running your application on more than one phone (as most carriers require that you do), put an emphasis on dynamic and relative layout structures. I promise, your screen size will change in a dramatic way later. Minimize the number of absolute X/Y values, and keep the ones you do use in easy-to-find locations. The next task is finding a list of all the relevant child elements that can be placed inside a layout. This resource is available in confusing documentation form at http://code.google.com/android/ reference/android/R.styleable.html#Menu. From there, you can move on to the first UI task: making a login screen for the “socially awkward” application. This login screen will become part of the getSplashy example application. Scrolling, Text Entry, Buttons, and All the Simple Things in Life It’s now time to put one of the layout classes to use. XML layouts are perfect for user input, information relay, and nearly anything where the contents of the screen are relatively static. You’ll add a simple login 52 Android Essentials screen to the aforementioned “socially awkward” application (see Code Listing 3-1). The first task is to describe what the screen will look like in a new view. You’ll use a linear layout so you can just add widgets vertically. (Note that this XML requires a general_bg image and the disclaimer string to be defined in the res folder. Download the project for this chapter from the Apress web site for more information.) Code Listing 3-1. /res/layout/login.xml Android Essentials 53
还剩117页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 5 人已下载

下载pdf

pdf贡献者

lotusct

贡献于2012-05-09

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf