RSS

Search Engine

Showing posts with label Android Application. Show all posts
Showing posts with label Android Application. Show all posts

Wednesday, October 6, 2010

Panoramio

The Panoramio web site has a great collection of photos from all over the world, and they also have a very convenient web API. I thought it would be a lot of fun to use Android to access this content while you are out walking around – and especially to have Android find interesting photos based on your current location. The resulting open source application is now available in the apps-for-android project.

The application starts by showing your current location in a custom MapView. You can pan and zoom this map to choose the area you want to search.

Once you have selected a search area, the application downloads thumbnails of the most popular photos taken within that area:

You can tap on an item to see more details.

From here you can use the menu to find more pictures by the same photographer or to see the original photo on the Panoramio site. My two favorite features, though, help you find the location of the photo in the real world. You can show the location on the map along with your current location:

Or, you can bring up a radar view that uses the compass and GPS to show you the location of the photo in relation to your own location:

I thought that other applications might want to use the radar view for their own purposes ("find me the nearest taqueria"), so I split that into its own package. You can find the source for that in the Radar project. Of course, you don't need the source in order to use this feature. Because of Android's component architecture, all you need to do us use an Intent:

Intent i = new Intent("com.google.android.radar.SHOW_RADAR");
i.putExtra("latitude", 37.422f);
i.putExtra("longitude", -122.084f);
startActivity(i);

The code for these applications is not very long, but they are a good example of how to use a number of Android's location APIs:

Friday, October 1, 2010

BioWallet, Secure Your Device!

BioWallet is a biometric authentication system for the Android platform that performs the identification of a user based on something that he/she is, other than something that he/she knows or owns. Currently, it supports iris and handwritten signature based authentication. BioWallet is also a password and secrets manager that stores the sensitive information using strong biometric encryption techniques and allows the user to recover them without
remembering any password.

BioWallet is basically a “safe wallet” that can store sensitive information (passwords, certificates, documents, conversations, notes, pictures, etc.). This information can only be accessed to by the user resenting his/her iris or signing in the touch screen. The data is kept secret even if the device is lost or stolen using strong biometric encryption techniques.

The user doesn’t need to worry anymore remembering a lot of different and difficult passwords or using the same simple password for everything.

Advanced users can also digitally sign their ocuments or encrypt the communications.


HIGHLY SECURE


The information is encrypted with military grade security (AES 256 bits) and only can be decrypted using your biometric characteristics. The encryption is even resistant to brute-force and dictionary based attacks because the internal keys used are generated long enough and completely random to prevent them.



EASY TO USE


The application has been designed to be both powerful and easy to use by non technical people. Two different profiles will give you what you want: power and flexibility for “advanced” users and ease to be used by people that “just want it to work”.



PROTECTS YOUR PRIVACY


Your biometric data are never sent outside your mobile device or stored, so they are safe even if the phone is lost or stolen.

Thursday, September 30, 2010

Divide and Conquer

Years ago I was addicted to a simple game that I played on my then state-of-the-art Pentium-75. In this game, balls would bounce around, and I would try to partition them into small enough spaces so that I could go to the next level where more and more balls would be added. As of a couple of months ago, for the life of me, I couldn't remember the name of this game. So when I sat down to write an application for Android in my 20% time, I thought, why not try to recreate something similar? After completing most of the game and showing it to some of my friends at work, one of them said, "Oh, this reminds me of JezzBall!" Eureka! If working on this game does nothing more than reveal the name of one of the favorite games of my youth, I'll call it a success, but in the meantime, I'm happy to announce that the source of this application, named Divide and Conquer, is now available on apps-for-android.

The game starts with instructions:

and begins simply enough with one ball bouncing around. You drag your finger in a horizontal or vertical gesture on the screen to initiate a line that extends until it reaches the edges:

In each level, once you've shaved off 80% of the original area, you move to the next level. Here's a screen shot of level 6:

If a ball hits a line in progress, you lose a life:

Once you are out of lives, it is game over:

While this game isn't going to win any awards for cutting edge graphics, it demonstrates use of several Android features and APIs:

  • custom drawing and animation
  • touch input based gesture detection
  • overriding the default behavior of the back key in some situations (to pause the game)
  • creating custom Dialogs
  • configuring an application to be full screen with no title or status bar
  • use of the preferences framework
  • use of the vibrator API

DivideAndConquerView is a custom View that implements its own onDraw method using the Canvas methods, and gesture detection using onTouchEvent and a helper class DirectionPoint. It keeps track of the state of the game using BallEngine, and reports relevant events back to the main activity of the application, which, in turn, keeps track of and controls the state of the game. The application is configured to be full screen in its AndroidManifest.xml file.

Three new Samples: Triangle, SpriteText and Downloader

I've posted three new open source samples to the apps-for-android project: Triangle, SpriteText and Downloader.

The first two samples, Triangle and SpriteText, show techniques that would be useful to anyone using the OpenGL ES 3D graphics APIs to write Android applications. The samples contain several reusable classes that may eventually be incorporated (in some form) into the SDK. Chief among these is the GLView class, which abstracts the OpenGL ES book-keeping code from the rest of the application. GLView helps handle the extra work OpenGL ES applications have to do when the activity is paused and resumed, and when the display goes to sleep and wakes up. In the Pause/Resume case the OpenGL surface has to be recreated. In the display sleep / wake-up case the entire OpenGL context has to be recreated.

Triangle

The first sample, Triangle, shows how to use the GLView class and the OpenGL ES 3D library to display a spinning textured triangle. Think of it as the "hello, world" of OpenGL ES apps. Because it's relatively simple, it's a good place to start when experimenting with the OpenGL ES API.

SpriteText

The second sample, SpriteText, shows how to efficiently display screen-aligned text using the GL11Ext.glDrawTexiOES method. SpriteText contains a reusable LabelMaker class for drawing static text and screen-aligned images, as well as a Projector class for finding the 2D screen coordinates corresponding to a 3D point, and a MatrixTrackingGL class for keeping track of the current transformation matrix. Finally, it shows how to use these classes to display a milliseconds per frame counter. A ms/f counter can be helpful for tuning graphics performance.

Downloader

The third sample, Downloader, shows how to add a downloader activity to your application. The downloader activity runs at the beginning of your application and makes sure that a set of files have been downloaded from a web server to the phone's SD card. Downloader is useful for applications that need more local data than can fit into an .apk file. For example a game could use Downloader to download the game's artwork, sound effects, and level data. The Downloader activity is designed to be a drop-in addition to your application. You customize it by supplying the URL of an XML configuration file which lists the data files that need to be downloaded.

Tuesday, September 28, 2010

Fancy ListViews Redux: 0.9 SDK and RatingBar

You may remember way back when (e.g., July 2008) when Building ‘Droids featured a six-post series on creating fancy ListView implementations, culminating in a CheckListView widget that could be used as a drop-in replacement for ListView.

They’re ba-ack!

Specifically, today, let’s take a look at the 0.9 SDK’s impact on the Fancy ListView series, and update one of the examples to take advantage of a new widget: the RatingBar.

In many respects, the code and techniques introduced in the Fancy ListViews series are still valid and relevant in the world of the 0.9 SDK. Perhaps the biggest change is that ViewInflate became LayoutInflater, requiring some search-and-replace edits of your older M5 source code. But the concepts of supplying custom views, of recycling views using the ViewHolder/ViewWrapper pattern, and the like are still very useful, even after the 0.9 SDK.

Of course, as promised, the 0.9 SDK has its own take on having checkable ListView widgets; this will be covered in a future blog post.

For now, though, let’s revisit the last sample from the Fancy ListViews series and update it to eschew the checkbox, switching to the new RatingBar.

RatingBar is a widget designed to users to provide a rating to something, such as rating a music track or a blog post or an Android development book up on Amazon.com (*cough*). A rating is a float, from 0 to a specified maximum (android:numStars). You can specify what the granularity of the rating is (android:stepSize) and whether it is merely an indicator (android:isIndicator) or if it is a user-input element to allow users to set the rating by sliding their finger across the stars.

As with just about any moderately-sized widget, the RatingBar can be used as part of a row in a ListView. In fact, replacing the CheckBox in CheckListView with a RatingBar is fairly straight-forward:

  1. public class RateableWrapper extends AdapterWrapper {
  2. Context ctxt=null;
  3. float[] rates=null;
  4. public RateableWrapper(Context ctxt, ListAdapter delegate) {
  5. super(delegate);
  6. this.ctxt=ctxt;
  7. this.rates=new float[delegate.getCount()];
  8. for (int i=0;i
  9. this.rates[i]=2.0f;
  10. }
  11. }
  12. public View getView(int position, View convertView, ViewGroup parent) {
  13. ViewWrapper wrap=null;
  14. View row=convertView;
  15. if (convertView==null) {
  16. LinearLayout layout=new LinearLayout(ctxt);
  17. RatingBar rate=new RatingBar(ctxt);
  18. rate.setNumStars(3);
  19. rate.setStepSize(1.0f);
  20. View guts=delegate.getView(position, null, parent);
  21. layout.setOrientation(LinearLayout.HORIZONTAL);
  22. rate.setLayoutParams(new LinearLayout.LayoutParams(
  23. LinearLayout.LayoutParams.WRAP_CONTENT,
  24. LinearLayout.LayoutParams.FILL_PARENT));
  25. guts.setLayoutParams(new LinearLayout.LayoutParams(
  26. LinearLayout.LayoutParams.FILL_PARENT,
  27. LinearLayout.LayoutParams.FILL_PARENT));
  28. RatingBar.OnRatingBarChangeListener l=new RatingBar.OnRatingBarChangeListener() {
  29. public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromTouch) {
  30. rates[(Integer)ratingBar.getTag()]=rating;
  31. }
  32. };
  33. rate.setOnRatingBarChangeListener(l);
  34. layout.addView(rate);
  35. layout.addView(guts);
  36. wrap=new ViewWrapper(layout);
  37. wrap.setGuts(guts);
  38. layout.setTag(wrap);
  39. rate.setTag(new Integer(position));
  40. rate.setRating(rates[position]);
  41. row=layout;
  42. }
  43. else {
  44. wrap=(ViewWrapper)convertView.getTag();
  45. wrap.setGuts(delegate.getView(position, wrap.getGuts(), parent));
  46. wrap.getRatingBar().setTag(new Integer(position));
  47. wrap.getRatingBar().setRating(rates[position]);
  48. }
  49. return(row);
  50. }
  51. }
  • Create and configure the RatingBar when it is lazy-instantiated in getView()
  • Hook in a RatingBar.OnRatingBarChangeListener to update a float[] of ratings, rather than a boolean[] of checkbox states
  • Update the rating in the RatingBar when it is recycled
  • Rename all the classes to something more logical (e.g., CheckableWrapper becomes RateableWrapper)

Unfortunately, the stock style for RatingBar is a bit big for lists:

A sample ListView using a RatingBar in each row

You can use a ratingBarStyleSmall style to shrink the size, but then the RatingBar becomes read-only, meaning you would need some other means to let people specify the rating itself.

Monday, September 20, 2010

Separating Lists with Headers in Android 0.9

Earlier today the latest Android 0.9 SDK was released, and it’s packed full of wonderful changes. As you play around, you might see ListViews split into sections using separating headers. (Example shown on the right is the browser settings list.)

There isn’t an easy way of creating these separated lists, so I’ve put together SeparatedListAdapter which does it quickly. To summarize, we’re creating a new BaseAdapter that can contain several other Adapters, each with their own section headers.

First let’s create some simple XML layouts to be used for our lists: first the header view, then two item views that we’ll use later for the individual lists. (Thanks to Romain Guy for helping me find existing styles to keep these XML layouts nice and tidy.)


  1. <TextView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/list_header_title"
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:paddingTop="2dip"
  7. android:paddingBottom="2dip"
  8. android:paddingLeft="5dip"
  9. style="?android:attr/listSeparatorTextViewStyle" />


  10. <TextView
  11. xmlns:android="http://schemas.android.com/apk/res/android"
  12. android:id="@+id/list_item_title"
  13. android:layout_width="fill_parent"
  14. android:layout_height="fill_parent"
  15. android:paddingTop="10dip"
  16. android:paddingBottom="10dip"
  17. android:paddingLeft="15dip"
  18. android:textAppearance="?android:attr/textAppearanceLarge"
  19. />


  20. <LinearLayout
  21. xmlns:android="http://schemas.android.com/apk/res/android"
  22. android:layout_width="fill_parent"
  23. android:layout_height="wrap_content"
  24. android:orientation="vertical"
  25. android:paddingTop="10dip"
  26. android:paddingBottom="10dip"
  27. android:paddingLeft="15dip"
  28. >
  29. <TextView
  30. android:id="@+id/list_complex_title"
  31. android:layout_width="fill_parent"
  32. android:layout_height="wrap_content"
  33. android:textAppearance="?android:attr/textAppearanceLarge"
  34. />
  35. <TextView
  36. android:id="@+id/list_complex_caption"
  37. android:layout_width="fill_parent"
  38. android:layout_height="wrap_content"
  39. android:textAppearance="?android:attr/textAppearanceSmall"
  40. />
  41. LinearLayout>

Now let’s create the actual SeparatedListAdapter class which provides a single interface to multiple sections of other Adapters. After using addSection() to construct the child sections, you can easily use ListView.setAdapter() to present the now-separated list to users.

As for the Adapter internals, to correctly find the selected item among the child Adapters, we walk through subtracting from the original position until we find either a header (position = 0) or item in the current child Adapter (position <>

Here’s the source for SeparatedListAdapter:

  1. public class SeparatedListAdapter extends BaseAdapter {

  2. public final Map sections = new LinkedHashMap();
  3. public final ArrayAdapter headers;
  4. public final static int TYPE_SECTION_HEADER = 0;

  5. public SeparatedListAdapter(Context context) {
  6. headers = new ArrayAdapter(context, R.layout.list_header);
  7. }

  8. public void addSection(String section, Adapter adapter) {
  9. this.headers.add(section);
  10. this.sections.put(section, adapter);
  11. }

  12. public Object getItem(int position) {
  13. for(Object section : this.sections.keySet()) {
  14. Adapter adapter = sections.get(section);
  15. int size = adapter.getCount() + 1;

  16. // check if position inside this section
  17. if(position == 0) return section;
  18. if(position < class="keyword">return adapter.getItem(position - 1);

  19. // otherwise jump into next section
  20. position -= size;
  21. }
  22. return null;
  23. }

  24. public int getCount() {
  25. // total together all sections, plus one for each section header
  26. int total = 0;
  27. for(Adapter adapter : this.sections.values())
  28. total += adapter.getCount() + 1;
  29. return total;
  30. }

  31. public int getViewTypeCount() {
  32. // assume that headers count as one, then total all sections
  33. int total = 1;
  34. for(Adapter adapter : this.sections.values())
  35. total += adapter.getViewTypeCount();
  36. return total;
  37. }

  38. public int getItemViewType(int position) {
  39. int type = 1;
  40. for(Object section : this.sections.keySet()) {
  41. Adapter adapter = sections.get(section);
  42. int size = adapter.getCount() + 1;

  43. // check if position inside this section
  44. if(position == 0) return TYPE_SECTION_HEADER;
  45. if(position < class="keyword">return type + adapter.getItemViewType(position - 1);

  46. // otherwise jump into next section
  47. position -= size;
  48. type += adapter.getViewTypeCount();
  49. }
  50. return -1;
  51. }

  52. public boolean areAllItemsSelectable() {
  53. return false;
  54. }

  55. public boolean isEnabled(int position) {
  56. return (getItemViewType(position) != TYPE_SECTION_HEADER);
  57. }

  58. @Override
  59. public View getView(int position, View convertView, ViewGroup parent) {
  60. int sectionnum = 0;
  61. for(Object section : this.sections.keySet()) {
  62. Adapter adapter = sections.get(section);
  63. int size = adapter.getCount() + 1;

  64. // check if position inside this section
  65. if(position == 0) return headers.getView(sectionnum, convertView, parent);
  66. if(position < class="keyword">return adapter.getView(position - 1, convertView, parent);

  67. // otherwise jump into next section
  68. position -= size;
  69. sectionnum++;
  70. }
  71. return null;
  72. }

  73. @Override
  74. public long getItemId(int position) {
  75. return position;
  76. }

  77. }

As expected, it correctly prevents the section headers from being selected, and seamlessly stiches together the various Adapters.

This approach also uses convertView correctly as long as the child Adapters return getItemViewType() and getViewTypeCount() normally. No special changes are needed for an Adapter to become a child.

Now let’s use SeparatedListAdapter in some example code. We use the XML layouts defined earlier to create an ArrayAdapter and an advanced two-row SimpleAdapter, and then add both as sections to our SeparatedListAdapter.

  1. public class ListSample extends Activity {

  2. public final static String ITEM_TITLE = "title";
  3. public final static String ITEM_CAPTION = "caption";

  4. public Map createItem(String title, String caption) {
  5. Map item = new HashMap();
  6. item.put(ITEM_TITLE, title);
  7. item.put(ITEM_CAPTION, caption);
  8. return item;
  9. }

  10. @Override
  11. public void onCreate(Bundle icicle) {
  12. super.onCreate(icicle);

  13. List> security = new LinkedList>();
  14. security.add(createItem("Remember passwords", "Save usernames and passwords for Web sites"));
  15. security.add(createItem("Clear passwords", "Save usernames and passwords for Web sites"));
  16. security.add(createItem("Show security warnings", "Show warning if there is a problem with a site's security"));

  17. // create our list and custom adapter
  18. SeparatedListAdapter adapter = new SeparatedListAdapter(this);
  19. adapter.addSection("Array test", new ArrayAdapter(this,
  20. R.layout.list_item, new String[] { "First item", "Item two" }));
  21. adapter.addSection("Security", new SimpleAdapter(this, security, R.layout.list_complex,
  22. new String[] { ITEM_TITLE, ITEM_CAPTION }, new int[] { R.id.list_complex_title, R.id.list_complex_caption }));

  23. ListView list = new ListView(this);
  24. list.setAdapter(adapter);
  25. this.setContentView(list);

  26. }

  27. }

The resulting interface behaves just like the browser preferences list, and you could easily create other custom Adapters to insert into the various sections, such as including icons or checkboxes.

These section headers can really help separate out otherwise-cluttered activities. I used them several places in my CompareEverywhere application which lets you easily compare prices and read reviews for any product with a ba

Amarok 1.4 remote in Android using DCOP+Python

We’ve all seen the Apple app that lets you control iTunes remotely, and there totally needs to be something like this for Android. I’m an avid fan of Amarok, so I’ve whipped together a simple remote control that runs on Android. It only took about 3 hours tonight, and I’m releasing everything GPLv3 here. First some details on the architecture:

Amarok can easily be controlled via DCOP, including fetching currently playing information and album art. DCOP is a local, safe IPC architecture that usually comes already enabled with KDE applications. Just a reminder that DCOP is being phased out in KDE 4 with D-Bus taking its place. Speaking of the D-Bus future, there is an awesome standard called Media Player Remote Interface Specification (MPRIS) that is being put together by the folks at XMMS, VLC, Amarok, and others. It doesn’t seem to be in stable releases yet, but will be soon. Back to the present, I’m going to just focus on getting Amarok 1.4 working with older DCOP calls.

First, I built a Python bridge that offers a simple HTTP REST-like API that will help us relay pre-approved DCOP commands from Android to Amarok. The Python is pretty simple:

  1. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
  2. import pydcop, re
  3. port = 8484
  4. allowed = ["status", "trackCurrentTime", "trackTotalTime", "album", "artist",
  5. "title", "next", "playPause", "prev", "volumeDown", "volumeUp",
  6. "coverImage", "seek"]
  7. resafe = re.compile("[^A-Za-z0-9/]")
  8. class AmarokHandler(BaseHTTPRequestHandler):
  9. def do_GET(self):
  10. # pull out action and simple variable
  11. safe = resafe.sub('', self.path)
  12. blank, action, var = tuple(safe.split('/'))
  13. # skip if action has not been approved
  14. if not action in allowed: return
  15. # check if image request
  16. if action == "coverImage":
  17. self.send_response(200)
  18. self.send_header('Content-type', 'image/jpeg')
  19. self.end_headers()
  20. cover = open((pydcop.DCOPMethod("amarok", "player", "coverImage"))())
  21. self.wfile.write(cover.read())
  22. cover.close()
  23. return
  24. # make dcop call over to amarok
  25. if len(var) > 0: reply = (pydcop.DCOPMethod("amarok", "player", action))(int(var))
  26. else: reply = (pydcop.DCOPMethod("amarok", "player", action))()
  27. # write back any dcop response
  28. self.send_response(200)
  29. self.send_header('Content-type', 'text/plain')
  30. self.end_headers()
  31. self.wfile.write(reply)
  32. return
  33. try:
  34. server = HTTPServer(('', port), AmarokHandler)
  35. print 'started amarokremote server on port %s' % (port)
  36. server.serve_forever()
  37. except KeyboardInterrupt:
  38. server.socket.close()

Essentially we are using a URL of the form http://ipaddress:port/command/variable. For example, in the Android emulator you could call http://10.0.2.2:8282/volumeUp/ to increase the volume.

Carefully note that we are screening the commands against an “allowed” list before running off to Amarok with them. We’re also scrubbing the incoming calls to prevent any buffer overflows. Usually we are just calling the Amarok DCOP method and returning any result. One exception is for coverImage requests, because Amarok just returns a local path. We help that image over the Python bridge by sending the local JPEG as the response.

On the Android side of things, we have a simple screen layout and are hooking up the various API calls to buttons. There’s also a background thread that keeps Android in-sync with what Amarok is currently playing. Finally, we’re using some simple preferences to store the server string and update interval. (Tap the menu button to change these settings.)

Here’s a tarball of the Eclipse project, along with the APK that’s ready to run on the new 0.9 SDK. Start the Python server above on your the computer running Amarok, change the IP address if needed, and you should be ready to go.

Also, a reminder that 10.0.2.2 is magic on the Android emulator because it points to the loopback adapter of the parent computer. So, if you’re running the emulator and Amarok on the same computer, this IP address will work perfectly.

Saturday, September 18, 2010

Fun with Fonts

Inevitably, you’ll get the question “hey, can we change this font?” when doing application development. The answer depends on what fonts come with the platform, whether you can add other fonts, and how to apply them to the widget or whatever needs the font change.

Android is no different. It comes with some fonts plus a means for adding new fonts. Though, as with any new environment, there are a few idiosyncrasies to deal with.

Android natively knows three fonts, by the shorthand names of “sans”, “serif”, and “monospace”. These fonts are actually the Droid series of fonts, created for the Open Handset Alliance by Ascender.

For those fonts, you can just reference them in your layout XML, if you choose, such as:

  1. xml version="1.0" encoding="utf-8"?>
  2. <TableLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. android:stretchColumns="1">
  7. <TableRow>
  8. <TextView
  9. android:text="sans:"
  10. android:layout_marginRight="4px"
  11. android:textSize="20sp"
  12. />
  13. <TextView
  14. android:id="@+id/sans"
  15. android:text="Hello, world!"
  16. android:typeface="sans"
  17. android:textSize="20sp"
  18. />
  19. TableRow>
  20. <TableRow>
  21. <TextView
  22. android:text="serif:"
  23. android:layout_marginRight="4px"
  24. android:textSize="20sp"
  25. />
  26. <TextView
  27. android:id="@+id/serif"
  28. android:text="Hello, world!"
  29. android:typeface="serif"
  30. android:textSize="20sp"
  31. />
  32. TableRow>
  33. <TableRow>
  34. <TextView
  35. android:text="monospace:"
  36. android:layout_marginRight="4px"
  37. android:textSize="20sp"
  38. />
  39. <TextView
  40. android:id="@+id/monospace"
  41. android:text="Hello, world!"
  42. android:typeface="monospace"
  43. android:textSize="20sp"
  44. />
  45. TableRow>
  46. <TableRow>
  47. <TextView
  48. android:text="Custom:"
  49. android:layout_marginRight="4px"
  50. android:textSize="20sp"
  51. />
  52. <TextView
  53. android:id="@+id/custom"
  54. android:text="Hello, world!"
  55. android:textSize="20sp"
  56. />
  57. TableRow>
  58. TableLayout>

This layout builds a table showing short samples of four fonts. Notice how the first three have the android:typeface attribute, whose value is one of the three built-in font faces (e.g., “sans”).

The three built-in fonts are very nice. However, it may be that a designer, or a manager, or a customer wants a different font than one of those three. Or perhaps you want to use a font for specialized purposes, such as a “dingbats” font instead of a series of PNG graphics.

The easiest way to accomplish this is to package the desired font(s) with your application. To do this, simply create an assets/ folder in the project root, and put your fonts (in TrueType, or TTF, form) in the assets. You might, for example, create assets/fonts/ and put your TTF files in there.

Then, you need to tell your widgets to use that font. Unfortunately, you can no longer use layout XML for this, since the XML does not know about any fonts you may have tucked away as an application asset. Instead, you need to make the change in Java code:

  1. public class FontSampler extends Activity {
  2. @Override
  3. public void onCreate(Bundle icicle) {
  4. super.onCreate(icicle);
  5. setContentView(R.layout.main);

  6. TextView tv=(TextView)findViewById(R.id.custom);
  7. Typeface face=Typeface.createFromAsset(getAssets(), "fonts/HandmadeTypewriter.ttf");

  8. tv.setTypeface(face);
  9. }
  10. }

Here, we grab the TextView for our “custom” sample, then create a Typeface object via the static createFromAsset() builder method. This takes the application’s AssetManager (from getAssets()) and a path within your assets/ directory to the font you want.

Then, it is just a matter of telling the TextView to setTypeface(), providing the Typeface you just created. In this case, we are using the Handmade Typewriter font.

The results?

Font Sampler

Note that Android does not seem to like all TrueType fonts. I spent a lot of time trying to figure out what I was doing wrong with this sample, before switching to a different font, and everything “just worked”. When Android dislikes a custom font, rather than raise an Exception, it seems to substitute Droid Sans (”sans”) quietly. So, if you try to use a different font and it does not seem to be working, it may be that the font in question is incompatible with Android, for whatever reason.

Also note that TrueType fonts can be rather pudgy, particularly if they support an extensive subset of the available Unicode characters. The Handmade Typewriter font used here runs over 70KB; the DejaVu free fonts can run upwards of 500KB apiece. Even compressed, these add bulk to your application, so be careful not to go overboard with custom fonts, lest your application take up too much room on your users’ phones.

Friday, September 17, 2010

Phonecalls

Again, there was a bit of a break in the blog. One reason is personal: I moved to London to take a new job. The other reason is that this time I went into the hairy issue which is call handling in Android.

I worked with the m3-rc37a version of the emulator and I can tell you that emulator crashes are really common if you play with the telephony stuff in this version. Don't be surprised if the example program distributed with this blog entry does not show the behaviour I promise but crashes instead. It just happens with this emulator version.



You can find the example program here.

The task I gave myself is catching incoming and outgoing calls which is easy. There needs to be just an IntentReceiver defined in the manifest and the onReceiveIntent method implemented in the intent receiver class. The manifest entry declares the filter for the IntentReceiver (again, the XML is mangled due to limitations of the blog engine).

[receiver class=".PhoneIntentReceiver"]
[intent-filter]
[action android:value="android.intent.action.PHONE_STATE" /]
[/intent-filter]
[/receiver]


PHONE_STATE is a general intent for signalling all sorts of phone events. The example program dumps the event bundle when the event comes in. In case of outgoing call, the event is delivered with state=OFFHOOK when the call starts then state=IDLE when the call finishes (state is a key in the intent bundle ("extras") and its string values are OFFHOOK and IDLE). In case of the incoming call (you can simulate it by telnet localhost 5554 then issuing the gsm call phone_number command on the console) the state transition is RINGING then IDLE again.

So far so good. Having a notification on incoming and outgoing calls is less useful, however, if the caller or the called number is not known. This turned out to be a really hairy issue for which I did not find the answer (just desperate help requests). I remembered the good old Series60 days and went for the call log.

The call log is surprisingly co-located in the database of the Contacts application. Try this:

adb shell
#cd /data/data/com.google.android.providers.contacts/databases

# sqlite3 contacts.db

SQLite version 3.5.0
Enter ".help" for instructions
sqlite> .dump

...

CREATE TABLE calls (_id INTEGER PRIMARY KEY,number TEXT,number_key TEXT,number_type TEXT,date INTEGER,duration INTEGER,type INTEGER,person INTEGER,new INTEGER);

INSERT INTO "calls" VALUES(1,'+44444','44444+','Home',1200263250247,0,3,NULL,0);
INSERT INTO "calls" VALUES(2,'+4444445','5444444+','Home',1200263859817,5,2,NULL,1);
INSERT INTO "calls" VALUES(3,'+1234','4321+','Home',1200263990161,2,2,NULL,1);


There is the log. Unfortunately (or fortunately? :-)) Android enforces strict database separation for applications so the Contacts database cannot be accessed from within another Android application. Luckily, the Contacts application decided to share the data as Content Provider. There is even a facilitator class: CallLog.Calls. Dumping the call log is relatively easy after that (look at the PhoneIntent class which is an Activity that makes this dump on pressing a button).

Now the only thing remained to see how the database is updated with regards to phone calls. It turned out that the number is not visible when the call goes out or comes in (RINGING or OFFHOOK state) but is accessible through the content provider when the IDLE transition event comes in. I was not able, therefore, to capture the number of the currently ongoing call but I was able to capture the number of the call that has just finished.

Testing the example application was a pain for me. When working with calls, the emulator tended to crash on my PC without installing any applications. In order to monitor the event transitions, you have to bring up the phone application because it does not come to the front by itself. You can do that by pressing some digits at the emulator's opening screen. If you don't bring the phone app to front manually, you won't be able to answer or reject the call.




I propose that you don't run adb logcat while the call goes on - it tends to increase the chance of emulator crash. You can examine whether the number was retrieved succesfully from the call log by running adb logcat after the call was finished.

Flipping Your Views

Let’s face it: phones are small.

Even if you have a phone with excellent screen resolution, the physical screen size is still rarely over 3″x5″, since most people want phones that can fit in a pocket, purse, pouch, or poncho.

This means your Android activities can only display so much stuff at one time without individual widgets or text getting too small to be easily read. There are any number of ways to handle this:

  • You could break the one activity into several…but this can get cumbersome
  • You can use ScrollView to allow users to scroll through a longer activity…but this may or may not be easy for the user depending on the device (e.g., a haptic interface using a swipe for scrolling assumes there’s a place clear in the UI for them to actually swipe without accidentally toggling a button or something)

  • You can use TabView, as described in the previous post…but you are locked into a particular presentation pattern (e.g., tabs)
  • You can use ViewFlipper and work out a UI pattern that’s right for you

Not surprisingly, today, we’re going to look at the latter option.

ViewFlipper inherits from FrameLayout, just like we used to describe the innards of a TabView in that previous post. However, initially, it just shows the first child view. It is up to you to arrange for the views to flip, either manually by user interaction, or automatically via a timer.

For example, here is a layout for a simple activity using a Button and a ViewFlipper:

  1. xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <Button android:id="@+id/flip_me"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="Flip Me!"
  11. />
  12. <ViewFlipper android:id="@+id/details"
  13. android:layout_width="fill_parent"
  14. android:layout_height="fill_parent"
  15. >
  16. <TextView
  17. android:layout_width="fill_parent"
  18. android:layout_height="wrap_content"
  19. android:textStyle="bold"
  20. android:textColor="#FF00FF00"
  21. android:text="This is the first panel"
  22. />
  23. <TextView
  24. android:layout_width="fill_parent"
  25. android:layout_height="wrap_content"
  26. android:textStyle="bold"
  27. android:textColor="#FFFF0000"
  28. android:text="This is the second panel"
  29. />
  30. <TextView
  31. android:layout_width="fill_parent"
  32. android:layout_height="wrap_content"
  33. android:textStyle="bold"
  34. android:textColor="#FFFFFF00"
  35. android:text="This is the third panel"
  36. />
  37. ViewFlipper>
  38. LinearLayout>

Notice that the layout defines three child views for the ViewFlipper, each a TextView with a simple message. Of course, you could have very complicated child views, if you so chose.

To manually flip the views, we need to hook into the Button and flip them ourselves when the button is clicked:

  1. public class FlipperDemo extends Activity {
  2. ViewFlipper flipper;

  3. @Override
  4. public void onCreate(Bundle icicle) {
  5. super.onCreate(icicle);
  6. setContentView(R.layout.main);

  7. flipper=(ViewFlipper)findViewById(R.id.details);

  8. Button btn=(Button)findViewById(R.id.flip_me);

  9. btn.setOnClickListener(new View.OnClickListener() {
  10. public void onClick(View view) {
  11. flipper.showNext();
  12. }
  13. });
  14. }
  15. }

This is just a matter of calling showNext() on the ViewFlipper, like you can on any ViewAnimator class.

The result is a trivial activity: click the button, and the next TextView in sequence is displayed, wrapping around to the first after viewing the last:

ViewFlipper demo, on the first view

Same demo, next view, after clicking button

This, of course, could be handled more simply by having a single TextView and changing the text and color on each click. However, you can imagine that the ViewFlipper contents could be much more complicated, like the contents you might put into a TabView.

As with the TabView in the previous post, sometimes, your ViewFlipper contents may not be known at compile time. As with TabView, though, you can add new contents on the fly with ease.

For example, let’s look at another sample activity, using this layout:

  1. xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <ViewFlipper android:id="@+id/details"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. >
  11. ViewFlipper>
  12. LinearLayout>

Notice that the ViewFlipper has no contents at compile time. Also note that there is no Button for flipping between the contents — more on this in a moment.

For the ViewFlipper contents, we will create large Button widgets, each containing one of the random words used in many an Android tutorial. And, we will set up the ViewFlipper to automatically rotate between the Button widgets, using an animation for transition:

  1. public class FlipperDemo2 extends Activity {
  2. static String[] items={"lorem", "ipsum", "dolor", "sit", "amet",
  3. "consectetuer", "adipiscing", "elit",
  4. "morbi", "vel", "ligula", "vitae",
  5. "arcu", "aliquet", "mollis", "etiam",
  6. "vel", "erat", "placerat", "ante",
  7. "porttitor", "sodales", "pellentesque",
  8. "augue", "purus"};
  9. ViewFlipper flipper;

  10. @Override
  11. public void onCreate(Bundle icicle) {
  12. super.onCreate(icicle);
  13. setContentView(R.layout.main);

  14. flipper=(ViewFlipper)findViewById(R.id.details);

  15. flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
  16. flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));

  17. for (String item : items) {
  18. Button btn=new Button(this);

  19. btn.setText(item);

  20. flipper.addView(btn,
  21. new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
  22. ViewGroup.LayoutParams.FILL_PARENT));
  23. }

  24. flipper.setFlipInterval(2000);
  25. flipper.startFlipping();
  26. }
  27. }

After getting our ViewFlipper widget from the layout, we first set up the “in” and “out” animations. In Android terms, an animation is a description of how a widget leaves (”out”) or enters (”in”) the viewable area. Animations are a complex beast, worthy of a blog post or two in their own right. For now, realize that animations are resources, stored in res/anim/ in your project. For this tutorial, we are using a pair of animations supplied by the SDK samples, available under the Apache 2.0 license. As their names suggest, widgets are “pushed” to the left, either to enter or leave the viewable area.

After iterating over the funky words, turning each into a Button, and adding the Button as a child of the ViewFlipper, we set up the flipper to automatically flip between children (flipper.setFlipInterval(2000);) and to start flipping (flipper.startFlipping();).

The result is an endless series of buttons, each appearing, then sliding out to the left after 1.5 seconds, being replaced by the next button in sequence, wrapping around to the first after the last has been shown:

Automatic ViewFlipper flip, shown in mid-animation

In this case, the above screenshot shows the ViewFlipper in mid-transition, animating away from one button to the next.

The auto-flipping ViewFlipper is useful for status panels or other situations where you have a lot of information to display, but not much room. The key is that, since it automatically flips between views, expecting users to interact with individual views is dicey — the view might switch away part-way through their interaction.

Tuesday, September 14, 2010

Adding Tabs Dynamically

The TabWidget in Android is set up to allow you to easily define tabs at compile time. However, sometimes, you want to add tabs to your activity during runtime. For example, imagine an email client where individual emails get opened in their own tab, for easy toggling between messages. In this case, you don’t know how many tabs or what their contents will be until runtime, when the user chooses to open a message.

Fortunately, Android also supports adding tabs dynamically at runtime, and this blog post will show you how.

For compile-time-defined tabs, in your Java code, to set up tabs with contents provided in layout XML, you simply create a TabHost.TabSpec object and call setContent() on it, with the widget ID of the tab’s contents:

  1. TabHost.TabSpec spec=tabs.newTabSpec("buttontab");

  2. spec.setContent(R.id.buttontab);
  3. spec.setIndicator("Button");
  4. tabs.addTab(spec);

Adding tabs dynamically at runtime works much the same way, except you use a different flavor of setContent(), one that takes a TabHost.TabContentFactory instance. This is just a callback that will be invoked — you provide an implementation of createTabContent() and use it to build and return the View that becomes the content of the tab.

Let’s take a look at an example.

First, here is some layout XML for an activity that sets up the tabs and defines one tab, containing a single button:

  1. xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent">
  6. <TabHost android:id="@+id/tabhost"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent">
  9. <TabWidget android:id="@android:id/tabs"
  10. android:layout_width="fill_parent"
  11. android:layout_height="wrap_content"
  12. />
  13. <FrameLayout android:id="@android:id/tabcontent"
  14. android:layout_width="fill_parent"
  15. android:layout_height="fill_parent"
  16. android:paddingTop="62px">
  17. <Button android:id="@+id/buttontab"
  18. android:layout_width="fill_parent"
  19. android:layout_height="fill_parent"
  20. android:text="A semi-random button"
  21. />
  22. FrameLayout>
  23. TabHost>
  24. LinearLayout>

What we want to do is add new tabs whenever the button is clicked. That can be accomplished in just a few lines of code:

  1. package com.commonsware.android.dynamictab;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.widget.AnalogClock;
  6. import android.widget.Button;
  7. import android.widget.TabHost;

  8. public class DynamicTabDemo extends Activity {
  9. @Override
  10. public void onCreate(Bundle icicle) {
  11. super.onCreate(icicle);
  12. setContentView(R.layout.main);

  13. final TabHost tabs=(TabHost)findViewById(R.id.tabhost);

  14. tabs.setup();

  15. TabHost.TabSpec spec=tabs.newTabSpec("buttontab");
  16. spec.setContent(R.id.buttontab);
  17. spec.setIndicator("Button");
  18. tabs.addTab(spec);

  19. tabs.setCurrentTab(0);

  20. Button btn=(Button)tabs.getCurrentView().findViewById(R.id.buttontab);

  21. btn.setOnClickListener(new View.OnClickListener() {
  22. public void onClick(View view) {
  23. TabHost.TabSpec spec=tabs.newTabSpec("tag1");

  24. spec.setContent(new TabHost.TabContentFactory() {
  25. public View createTabContent(String tag) {
  26. return(new AnalogClock(DynamicTabDemo.this));
  27. }
  28. });
  29. spec.setIndicator("Clock");
  30. tabs.addTab(spec);
  31. }
  32. });
  33. }
  34. }

In our button’s setOnClickListener() callback, we create a TabHost.TabSpec object and give it an anonymous TabHost.TabContentFactory. The factory, in turn, returns the View to be used for the tab — in this case, just an AnalogClock. The logic for constructing the tab’s View could be much more elaborate, such as using ViewInflate to construct a view from layout XML.

Initially, when the activity is launched, we just have the one tab:

Single static tab

But, if we click the button three times, though, we get three new tabs, each containing a clock:

Same activity after three button clicks

While in this case we added new tabs based on button clicks, you could use any sort of event as the trigger for adding a tab: long-tap on a listbox entry, option menu selection, etc.