Android Programming: Transferring Data Between Intents

April 18th, 2009

While working on the development of some new applications, I have tried transferring complex variables to new intents. For example:

Transferring a single variable, or set of single variable is easily accomplished by adding them to the intent.

   1: Intent i = new Intent();

   2: i.setClassName("packageName", "packageName.IntentClass");

   3: String term = "data to pass";

   4: i.putExtra("packageName.term", term);

   5: startActivity(i);

In the called intent, we then get this data by calling getExtras():

   1: Bundle extras = getIntent().getExtras();

   2: String term = extras.getString("packageName.term");

However, what if we want to pass more complex data? As you can see above, the data types that can be added as extras are somewhat limited. In order to pass an object, it must first implement either the Parcelable or Serializable class. Our next post will be on how to implement the Parcelable class, followed by a post on how to integrate a data service.

Android Programming: Basic Animation

December 30th, 2008

Saving space is a constant necessity of programming on mobile platforms. While mobile devices are extremely convenient, providing a large amount of computing power in an easy-to-carry size, the screen size can make it difficult to use complex applications. One benefit of both the iPhone/iPod and the Android platforms is the ability to use animations and views to provide additional screen real estate. On the Android platform, the easiest way to do this is by using the ViewFlipper widget. This is the method we use in our AgileMedCalc application to provide equation information along with the calculators.

To use this approach, the ViewFlipper widget is added similar to a layout in the application’s XML view. Each widget or layout within the ViewFlipper will be rotated through when the showNext() property is called by the application. Because of this, if you want to have more than one widget within a single view, you will need to have additional layouts added:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <RelativeLayout 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:     <ViewFlipper
   7:         android:id="@+id/flipper"
   8:         android:layout_width="fill_parent"
   9:         android:layout_height="fill_parent">
  10:         <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  11:             android:orientation="vertical"
  12:             android:layout_width="fill_parent"
  13:             android:layout_height="fill_parent">
  14:             <TextView
  15:                 android:id="@+id/albuminLbl"
  16:                 android:layout_width="wrap_content"
  17:                 android:layout_height="wrap_content"
  18:                 android:text="\nAlbumin (g/dL):"
  19:                 android:gravity="center_vertical" />
  20:             <EditText
  21:                 android:id="@+id/albumin"
  22:                 android:layout_width="fill_parent"
  23:                 android:layout_height="wrap_content"
  24:                 android:singleLine="true"
  25:                 android:layout_toRightOf="@+id/albuminLbl" />
  26:             <ImageView
  27:                 android:id="@+id/viewInfo"
  28:                 android:layout_width="wrap_content"
  29:                 android:layout_height="wrap_content"
  30:                 android:src="@drawable/questionicon"
  31:                 android:layout_below="@+id/calc"
  32:                 android:layout_alignParentRight="true" />
  33:         </RelativeLayout>
  34:         <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  35:             android:orientation="vertical"
  36:             android:layout_width="fill_parent"
  37:             android:layout_height="fill_parent">
  38:             <TextView
  39:                 android:id="@+id/calcInfo"
  40:                 android:layout_width="fill_parent"
  41:                 android:layout_height="wrap_content"
  42:                 android:text="Corrected Calcium = Measured Calcium + (0.8 * (Normal Albumin - Measured Albumin))\nNormal Albumin = 4.0" />
  43:             <ImageView
  44:                 android:id="@+id/done"
  45:                 android:layout_width="wrap_content"
  46:                 android:layout_height="wrap_content"
  47:                 android:src="@drawable/backicon"
  48:                 android:layout_below="@+id/calcInfo"
  49:                 android:layout_alignParentRight="true" />
  50:         </RelativeLayout>
  51:     </ViewFlipper>
  52: </RelativeLayout>

In the above code, our ViewFlipper has 2 RelativeLayouts. This allows us to switch between one layout or the other, thereby switching the entire layout’s contents with very little code. In our Java class, we add the following code to bind to our buttons (or in our case, an ImageView):

   1: private void setFlipper(){
   2:     ViewFlipper infoFlipper = (ViewFlipper)findViewById(R.id.flipper);
   3:     infoFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
   4:     infoFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));
   5:
   6:     final ImageView info = (ImageView)findViewById(R.id.viewInfo);
   7:     info.setOnClickListener(new View.OnClickListener() {
   8:         public void onClick(View v)
   9:         {
  10:             infoFlipper.showNext();
  11:         }
  12:     });
  13:     final ImageView done = (ImageView)findViewById(R.id.done);
  14:     done.setOnClickListener(new View.OnClickListener() {
  15:         public void onClick(View v)
  16:         {
  17:             infoFlipper.showNext();
  18:         }
  19:     });
  20: }

After adding our ViewFlipper object, we set a couple of animations on when the view moves in or out (we’ll talk more about this in a second). In addition, we set our onClickListeners to show the next view with infoFlipper.showNext(). This way, when a user clicks the icon, it will slide out the calculator and slide in the equation info.

This can be done either with or without fancy animations. Several animations are included with the Android SDK (SDKpath\samples\ApiDemos\res\anim). To use these, simply copy the animation from the SDK path into your application by creating the res\anim folder and pasting the animation file. Once this is done, you can access the animation like any other resource (such as R.anim.push_left_in). The details in these files can be customized to your liking to add different effects to the animation.

While the above examples are more complicated than doing a single view, it is much easier and saves a lot of typing when compared to making several different intents to display the same information. It also makes the applications look “shiny” which makes the GUI a little more friendly.

Android Programming: Google Service/App Authentication

December 23rd, 2008

While many of the Google applications already have Android-based ports, some of the more “specialty apps” such as AdSense don’t have a mobile interface. Luckily, the Google API allows for an easy login method using an HTTP Post request. One of our current applications, SimpleAdMonitor, uses this method to download an AdSense user’s daily and all time totals.

While the solution is fairly easy to implement, the actual POST request appears to run quite slowly, at least over the Android emulator and somewhat on the device as well.

The first step to generating an authorized session is to post the user’s login details to the Google ServiceLogin:

   1: String login = "https://www.google.com/accounts/ServiceLoginAuth?service=adsense&hl=en-US&ltmpl=login&ifr=true&passive=true&rm=hide&nui=3&alwf=true&continue=https%3A%2F%2Fwww.google.com%2Fadsense%2Fgaiaauth&followup=https%3A%2F%2Fwww.google.com%2Fadsense%2Fgaiaauth";
   2: DefaultHttpClient client = new DefaultHttpClient();
   3: String websiteData = null;
   4: URI uri = new URI(login);
   5: HttpPost method = new HttpPost(uri);
   6: method.addHeader("User-Agent", "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10");
   7: method.addHeader("Pragma", "no-cache");
   8: method.addHeader("Content-Type", "application/x-www-form-urlencoded");

On line 1, the address specifies a Google service to authorize against. This service can be changed if you need to access some other application. Once this request is formed, the user’s login information can be added using a NameValuePair list. These are then set on the HttpPost method using setEntity:

   1: List <NameValuePair> loginInfo = new ArrayList <NameValuePair>();
   2: loginInfo.add(new BasicNameValuePair("Email", username));
   3: loginInfo.add(new BasicNameValuePair("Passwd", password));
   4: loginInfo.add(new BasicNameValuePair("null", "Sign+in"));
   5: HttpEntity entity = new UrlEncodedFormEntity(nvps, HTTP.UTF_8);
   6:
   7: method.setEntity(entity);
   8: HttpResponse res = client.execute(method);

One item to note on line 3 is that the param used for the password is “Passwd”. Once this data is posted, the server will return a response that should be evaluated for a correct login. If the login is valid, the page should respond with a link to a redirect page that needs to again be posted to:

   1: String login2 = "https://www.google.com/accounts/CheckCookie?continue=https%3A%2F%2Fwww.google.com%2Fadsense%2Fgaiaauth&followup=https%3A%2F%2Fwww.google.com%2Fadsense%2Fgaiaauth&hl=en-US&service=adsense&ltmpl=login&chtml=LoginDoneHtml";
   2: method.setURI(new URI(login2));
   3: res = client.execute(method);

At this point, the server should respond with the data from the redirect URL, in this case, the main AdSense page. Further requests can now be made to the server using the same method without needing to re-authorize the user. As I mentioned previously, there are some latency issues with this method that I haven’t quite sorted out yet. The lag isn’t horrible, but is fairly significant when waiting for two fairly small pieces of data. Some additional digging will hopefully find out what is causing the delay and how to improve it.

Android Programming: DefaultHttpClient GET Request

December 16th, 2008

This article will briefly describe the method to form and receive an HTTP GET request in Android. The first code snippet listed below shows how to form the request and receive the data back from the server.  The second shows a function that can be used to read the InputStream and save it to a string. The entire process isn’t very difficult, but can be a bit confusing the first time you try to piece it together. The code below still requires some additional code to fail more elegantly in case of a broken internet connection, website error, etc., but should copy/paste pretty easily into applications.

   1: public String getUrlData(String url) {
   2:     String websiteData = null;
   3:     try {
   4:         DefaultHttpClient client = new DefaultHttpClient();
   5:         URI uri = new URI(url);
   6:         HttpGet method = new HttpGet(uri);
   7:         HttpResponse res = client.execute(method);
   8:         InputStream data = res.getEntity().getContent();
   9:         websiteData = generateString(data);
  10:     } catch (ClientProtocolException e) {
  11:         // TODO Auto-generated catch block
  12:         e.printStackTrace();
  13:     } catch (IOException e) {
  14:         // TODO Auto-generated catch block
  15:         e.printStackTrace();
  16:     } catch (URISyntaxException e) {
  17:         // TODO Auto-generated catch block
  18:         e.printStackTrace();
  19:     }
  20:
  21:     return websiteData;
  22: }
   1: public String generateString(InputStream stream) {
   2:     InputStreamReader reader = new InputStreamReader(stream);
   3:     BufferedReader buffer = new BufferedReader(reader);
   4:     StringBuilder sb = new StringBuilder();
   5:
   6:     try {
   7:         String cur;
   8:         while ((cur = buffer.readLine()) != null) {
   9:             sb.append(cur + "\n");
  10:         }
  11:     } catch (IOException e) {
  12:         // TODO Auto-generated catch block
  13:         e.printStackTrace();
  14:     }
  15:
  16:     try {
  17:         stream.close();
  18:     } catch (IOException e) {
  19:         // TODO Auto-generated catch block
  20:         e.printStackTrace();
  21:     }
  22:     return sb.toString();
  23: }