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.
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.
but I cancelled this and imported the original project.
0
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.
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.
I updated the AndroidManifest.xml file to declare the target will be latest API but still must work at the original version 8.
From
="1.0"="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
="1.0"="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"
<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.
<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)
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.
="1.0"="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
="1.0"="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.
@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);
canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
So I changed it to the following:
...
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
void showStatus()
{
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager =
(NotificationManager) pContext.getSystemService(ns);
int icon = R.drawable.notification_icon;
CharSequence tickerText = "balls = "+currCount;
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
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);
mNotificationManager.notify(HELLO_ID, notification);
}
And the new
void showStatus()
{
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager =
(NotificationManager) pContext.getSystemService(ns);
Notification notification = null;
Context context = pContext.getApplicationContext();
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
{
int icon = R.drawable.notification_icon;
long when = System.currentTimeMillis();
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();
}
mNotificationManager.notify(HELLO_ID, notification);
}
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.
public void onSensorChanged(SensorEvent event) {
long curTime = System.currentTimeMillis();
boolean shakeDetected = false;
if ((curTime - lastUpdate) > 100)
{
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
x = event.values[0];
y = event.values[1];
z = event.values[2];
here is the change to process x,y based on rotation
WindowManager w = getWindowManager();
mRotation = w.getDefaultDisplay().getRotation();
public void onSensorChanged(SensorEvent event) {
long curTime = System.currentTimeMillis();
boolean shakeDetected = false;
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];
x = event.values[1];
break;
}
z = event.values[2];
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