Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Updating an older API 8 app to API 18

4.53/5 (9 votes)
2 Jan 2014CPOL5 min read 20.5K   163  
Here is my experience of updating my application to a new platform and API

Pre-notes

I should state that the code and XML formatting is not looking correct to me. When I posted this using the Submission Wizard view, all looked ok, but after posting I can see errors. Hopefully this can be resolved soon and an update made.

Introduction

This will highlight my recent experience of updating an older Android application to both a new platform and API level. I will discuss both what went well and what changes were needed. But as per the custom here are the files of interest to download. Hopefully CodeProject editors and otherwise style and content purists will not be too harsh about the format my my article submission. I know that some people feel it should be all about code.

Background

Two years ago I wrote my first (and only) Android app and published it on CodeProject at http://www.codeproject.com/Articles/269318/An-Adventure-in-Porting-a-Java-Applet-to-Android-w

I have not updated this app since that time which was written on API level 8 for a Motorola cell phone running version 2.3.4

But this holiday I received a Galaxy tab 3 10.1 and decided to dust the cobwebs off the code and my brain cells and update this app to API level 18 to match the 4.2.2 version on this tablet.

Porting steps

The first step was to download the latest SDK onto my new PC https://developer.android.com/sdk/index.html

I ran the SDK Manager.exe and downloaded the default recommended 5 updates.

Image 1

Old metadata

I decided to delete the .metadata folder from my original project fearing that this data would be SDK dependent and may pollute the new project.

Open old project files

Then I opened Eclipse and pointed to my original project folder. At this point Eclipse is prompting for a new project.

Image 2

but I cancelled this and imported the original project.

0

Image 3

I won't belabor the details of importing a project since I assume you have the knowledge of doing this. In addition the steps are pretty simple.

First errors detected after import

After the import there were a few errors which is not surprising, I did not expect the code to simply build without some modifications.

Image 4

Specify new target

To fix the "Unable to resolve target 'android-10" I simply had to go to Project->Properties->Android and select the Android 4.4.2 version.

Image 5

I updated the AndroidManifest.xml file to declare the target will be latest API but still must work at the original version 8. From

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 android:versionCode="1"
 android:versionName="1.1" package="com.korsbergtools.ImpactPhysics">
<uses-sdk android:minSdkVersion="8" />

to

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.1" package="com.korsbergtools.ImpactPhysics">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" />

WebView namespace

There was an error in the WebView declaration, something about "Unexpected namespace"

XML
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/webviewhelp"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"></WebView>

I simply removed the namespace and solved that problem.

XML
<WebView
     android:id="@+id/webviewhelp"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"></WebView> 

Then there was an error on an apparent old ProGuard file. I simply deleted it.

And at this point the application actually worked on the old cell phone! Overall the process of moving this project/code to the new SDK and Eclipse was pretty simple.

Running Lint

While the app still worked on the cell phone, I also noticed several warnings and decided to try running the 'Lint' tool. I addressed some of these warnings and ignored others (for now)

Image 6

String resources

Some of the warnings were to suggest not using hard coded strings and replace with @string resources. Fortunately Eclipse has a feature to assist in refactoring these. Go to Refactor->Android->Extract Android String... It will convert the selected string into a resource located in the strings.xml file. Here is an portion of what options.xml looked like before the refactoring.

XML
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <TableLayout  android:id="@+id/LinearLayout1" android:orientation="vertical" 
              android:layout_width="match_parent" 
              android:layout_height="match_parent" 
              android:stretchColumns="1">
        <TableRow>
            <CheckBox android:id="@+id/checkBox7" android:text="Sound"></CheckBox>
            <Button  android:id="@+id/button2" android:text="Apply and exit"></Button>            
        </TableRow>

and after

XML
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <TableLayout  android:id="@+id/LinearLayout1" android:orientation="vertical" 
               android:layout_width="match_parent" 
               android:layout_height="wrap_content" 
               android:stretchColumns="1">
        <TableRow>
            <CheckBox android:id="@+id/checkBox7" 
               android:text="@string/sound"></CheckBox>
            <Button  android:id="@+id/button2" 
               android:text="@string/apply_and_exit"></Button>            
        </TableRow>
Runtime object allocation

Another warning was in ColorPickerDialog.java, it said "Avoid object allocations during draw/layout operations. Here is the offending code.

Java
@Override
protected void onDraw(Canvas canvas) {
    float r = mScrWidth - mPaint.getStrokeWidth()*0.5f;

    canvas.translate(mScrWidth, mScrWidth);
    canvas.drawOval(new RectF(-r, -r, r, r), mPaint); //offending new on each onDraw
    canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);

So I changed it to the following:

Java
...
//note from lint to pre-allocate
        //this code is located in the declaration of the ColorPickerView class
private RectF myRectF = new RectF();
...
@Override
protected void onDraw(Canvas canvas) {
    float r = mScrWidth - mPaint.getStrokeWidth()*0.5f;

    canvas.translate(mScrWidth, mScrWidth);
    myRectF.left = -r;
    myRectF.top = -r;
    myRectF.right = r;
    myRectF.bottom = r; 

    canvas.drawOval(myRectF, mPaint);
    canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
Api compatibility

There were several places were I was using deprecated API's. Here is one example of how that was addressed by doing a runtime check on the version and calling the correct code. Another solution would involve a bit more code by creating an abstract base class and then using a factory pattern to instantiate the correct subclass at runtime. However that decision is still based on the same examination of the Build version.

Here is the old code for showStatus

Java
void showStatus()
{
    //get ref to NotificationManager
    String ns = Context.NOTIFICATION_SERVICE;
    
    NotificationManager mNotificationManager = 
         (NotificationManager) pContext.getSystemService(ns);
    
    //instantiate it
    int icon = R.drawable.notification_icon;
    CharSequence tickerText = "balls = "+currCount;
    long when = System.currentTimeMillis();
    Notification notification = new Notification(icon, tickerText, when);
    
    //Define the notification's message and PendingIntent: 
    Context context = pContext.getApplicationContext();
    CharSequence contentTitle = "ImpactPhysics";
    CharSequence contentText = "Ball status";
    Intent notificationIntent = new Intent();
    PendingIntent contentIntent = 
         PendingIntent.getActivity(pContext, 0, notificationIntent, 0);

    
    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
    
    //Pass the Notification to the NotificationManager: 
    mNotificationManager.notify(HELLO_ID, notification);
}

And the new

Java
void showStatus()
{
    //get ref to NotificationManager
    String ns = Context.NOTIFICATION_SERVICE;
    
    NotificationManager mNotificationManager = 
          (NotificationManager) pContext.getSystemService(ns);
    Notification notification = null;
    Context context = pContext.getApplicationContext();
    

    // with android support library, we should be able to use new code
    // on old platforms 
    
    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
    {
        //instantiate it
        int icon = R.drawable.notification_icon;
        long when = System.currentTimeMillis();

        //Define the notification's message and PendingIntent: 
        CharSequence contentTitle = "ImpactPhysics";
        CharSequence contentText = "Ball status";
        Intent notificationIntent = new Intent();
        PendingIntent contentIntent = PendingIntent.getActivity(pContext, 0, notificationIntent, 0);
        
        CharSequence tickerText = "balls = "+currCount;
        notification = new Notification(icon, tickerText, when);
                notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
    }
    else
    {
      CharSequence countStr = "" + currCount;
      notification = new Notification.Builder(context)
         .setContentTitle("Number of balls = ")
         .setContentText(countStr)
         .build();   
    }
    
    //Pass the Notification to the NotificationManager: 
    mNotificationManager.notify(HELLO_ID, notification);
}    
//showStatus

Items not addressed that Lint found

There were several other items that were uncovered in the Lint analysis that I choose at least for now to ignore.

Icons and graphics

Most of the warnings pertained to the icon files. Either the files where duplicated (copy/paste) or missing. Since I have no skills or tools to draw good icons I simply choose to live with these warnings. A real application would want to handle these according to the Android design guidelines which I fully admit to never having read.

ShowDialog

Another thing found by Lint was my use of the deprecated showDialog() API. The recommended new approach is something called DialogFragment. But I looked into the code changes required for this and decided to skip that. I will live with the deprecation for now. Maybe later I will fix this and post an update.

Time to move onto new hardware platform

This same apk file actually ran without crashing on the new target (Samsung Galaxy tab 3 10.1). However it did not work quite correctly. This application uses the accelerometer to provide sort of a 'tilt ball table' effect. However on the table the orientation was all wrong. It turns out the solution was simple. I needed to read the rotation property for the device and handle the X and Y readings based on the orientation.

Here is the major portion of the old code.

Java
public void onSensorChanged(SensorEvent event) {
    long curTime = System.currentTimeMillis();
    boolean shakeDetected = false;
    // only allow one update every 100ms.
    if ((curTime - lastUpdate) > 100) 
    {
        long diffTime = (curTime - lastUpdate);
        lastUpdate = curTime;
 
        x = event.values[0];
        y = event.values[1];
        z = event.values[2];
        // process x,y,z... 
here is the change to process x,y based on rotation
Java
// setting mRotation is done elsewhere...
WindowManager w = getWindowManager();
mRotation = w.getDefaultDisplay().getRotation();

    public void onSensorChanged(SensorEvent event) {
	    long curTime = System.currentTimeMillis();
	    boolean shakeDetected = false;
	    // only allow one update every 100ms.
	    if ((curTime - lastUpdate) > 100) 
	    {
			long diffTime = (curTime - lastUpdate);
			lastUpdate = curTime;
	 
			switch (mRotation)
			{
			case Surface.ROTATION_0:
				x = event.values[0];
				y = event.values[1];				
				break;
				
			case Surface.ROTATION_90:
				y = event.values[0];
				x = event.values[1];				
				break;	
				
			case Surface.ROTATION_180:
				x = -event.values[0];
				y = -event.values[1];				
				break;	
				
			case Surface.ROTATION_270:
				y = -event.values[0]; //TODO swap x,y on tablet? and negate y?
				x = event.values[1];				
				break;	
			}

			z = event.values[2];
                        // process x,y,z...

Conclusion

Overall the code and project settings from the old version 2.3.4 to the new 4.2.2 API dealing with the small cell phone to large tablet form factor remained mostly unchanged. While I was researching what needed to be done I found the following web site base to be very useful http://developer.android.com

History

  • Version 1.3, Dec 2013

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)