Sunday, July 31, 2011

Android - creating a library project

Ok. The seemingly endless trek to getting this app launched continues. In my last post, we discovered that it would probably be worth our while to set up a project library in order to manage the paid vs. free version, as well as the multiple levels. Let's take a look at what the docs say about it.

http://developer.android.com/guide/developing/projects/projects-eclipse.html

Included topics are:

Setting up a Library Project
Referencing a Library Project

You can also designate an Android project as a library project, which allows it to be shared with other projects that depend on it. Once an Android project is designated as a library project, it cannot be installed onto a device.


Setting up a Library Project

A library project is a standard Android project, so you can create a new one in the same way as you would a new application project.

When you are creating the library project, you can select any application name, package, and set other fields as needed, as shown in figure 1.

Next, set the project's properties to indicate that it is a library project:

In the Package Explorer, right-click the library project and select Properties.
In the Properties window, select the "Android" properties group at left and locate the Library properties at right.
Select the "is Library" checkbox and click Apply.
Click OK to close the Properties window.




The new project is now marked as a library project. You can begin moving source code and resources into it, as described in the sections below.

You can also convert an existing application project into a library. To do so, simply open the Properties for the project and select the "is Library" checkbox. Other application projects can now reference the existing project as a library project.



// This is what we want to do.

Creating the manifest file

A library project's manifest file must declare all of the shared components that it includes, just as would a standard Android application. For more information, see the documentation for AndroidManifest.xml.

For example, the TicTacToeLib example library project declares the Activity GameActivity:


...

...

...



// Ok, I've got all that declared already.



Referencing a library project

If you are developing an application and want to include the shared code or resources from a library project, you can do so easily by adding a reference to the library project in the application project's Properties.

To add a reference to a library project, follow these steps:

In the Package Explorer, right-click the dependent project and select Properties.
In the Properties window, select the "Android" properties group at left and locate the Library properties at right.
Click Add to open the Project Selection dialog.
From the list of available library projects, select a project and click OK.
When the dialog closes, click Apply in the Properties window.
Click OK to close the Properties window.

As soon as the Properties dialog closes, Eclipse rebuilds the project, including the contents of the library project.

// That won't be a problem.

Declaring library components in the the manifest file

In the manifest file of the application project, you must add declarations of all components that the application will use that are imported from a library project. For example, you must declare any , , , , and so on, as well as , , and similar elements.

Declarations should reference the library components by their fully-qualified package names, where appropriate.

For example, the TicTacToeMain example application declares the library Activity GameActivity like this:


...

...

...



For more information about the manifest file, see the documentation for AndroidManifest.xml.
↑ Go to top


// Ok, I'll need to make the names fully qualified. But other than that, there doesn't seem to be too much heavy lifting as far as converting to a library project.

Now the question is, how to access the library project? The basic difference will really be that the validation code can't be included. So, the activity, StartActivity that calls the validation, will have to be a separate one. Or, I could have a Validate activity at the very begining, which calls the Start activity if successful. Or a BootStrapActivity that launches the start activity if it's the free version, and the validation activity if if paid. And the validation will launch the startActivity.

Ok, well, I suppose I should make sure that structure works before breaking out into separate projects.

package com.jlptquiz.app;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class BootStrapActivity extends Activity {

private static final String TAG = "BootStrapActivity";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.start_layout);
// Get the title text view

// pass control to Start activity
Intent i = new Intent(this,
StartActivity.class);

startActivity(i);

}
}

Ok, and change the start activity int the manifest, and add this:

android:screenOrientation="portrait">

Ok, let's see what happens...

Permission denial?

[2011-07-31 06:11:17 - JlptQuizApp] Starting activity com.jlptquiz.app.BootStrapActivity on device HT9CSP820421
[2011-07-31 06:11:18 - JlptQuizApp] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.jlptquiz.app/.BootStrapActivity }
[2011-07-31 06:11:18 - JlptQuizApp] ActivityManager: java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.jlptquiz.app/.BootStrapActivity } from null (pid=10618, uid=2000) requires null

And form logcat, ver similar...

I/ActivityManager( 101): Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.jlptquiz.app/.BootStrapActivity } from pid 10618
W/ActivityManager( 101): Permission denied: checkComponentPermission() reqUid=10061
W/ActivityManager( 101): Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.jlptquiz.app/.BootStrapActivity } from null (pid=10618, uid=2000) requires null

A post on SO says try uninstalling.

Hmm, still not good.

Well, let's change it back to start activity - ok, that works.

Ok, the problem turned out to be I had the activity named twice, once as the starting activity and once with a standard entry. So, *that* problem is solved.

Now, let's just give every activity a package name in the manifest.

Now, let's add the intent to to call StartActivity...

Crash, but that may be ok...yeah, I just forgot to add back in the declaration of StartActivity.

Ok, it seems to work ok, although the quiz start and end numbers somehow got reset to zero. I'll get to that later.

Ok, let's try out with all the startActivity code deleted, just this:


// pass control to Question activity
Intent i = new Intent(BootStrapActivity.this,
StartActivity.class);

startActivity(i);

Good! We're well on our way.

Ok, the next step is to make a copy of the project.

Now, let's make this a library, by following the steps described earlier in the post:


Next, set the project's properties to indicate that it is a library project:

In the Package Explorer, right-click the library project and select Properties.
In the Properties window, select the "Android" properties group at left and locate the Library properties at right.
Select the "is Library" checkbox and click Apply.
Click OK to close the Properties window.

That was easy.

Now, to reference it in the copied project...

To add a reference to a library project, follow these steps:

In the Package Explorer, right-click the dependent project and select Properties.
In the Properties window, select the "Android" properties group at left and locate the Library properties at right.
Click Add to open the Project Selection dialog.
From the list of available library projects, select a project and click OK.
When the dialog closes, click Apply in the Properties window.
Click OK to close the Properties window.

Ok, that copy is kind of full of errors. I think I'll just start a new Android project.

Ok, what should I specify as the min sdk?

http://phandroid.com/2010/11/02/77-of-android-phones-running-2-1-or-higher-1-5-close-to-being-obsolete/

As of Nov 2010, 9 months ago:

New platform figures have eeked out of Google today. According to their latest two-week ping leading up to November 1st, 77% of all Android phones run Android 2.1 or Android 2.2 (and Android 2.3, I suppose.) Specifically, 40.8% are running 2.1, and 36.2% are on 2.2. That still leaves 23% of phones on Android 1.6 or lower, but at least that number is getting smaller as each month goes by. 15% are on Android 1.6 while only 7.9 are on Android 1.5. With a majority of Motorola MOTOBLUR device owners around the world eventually being stepped up to 2.1, we don’t expect to see Android 1.5 lingering around too much longer. Google for the chart.

So, at that time, there were 23% 1.6 or lower, and 8% 1.5. So 1.5 is definitely out. Should I support 1.6? I'll go with that.

Ok. Ah, I see. When you add an imported library, Eclipse gives you access to the source of that library, so you don't have to switch to it. That's helpful.

Ok, we're getting there. Now, there were a few things I needed to change in the manifest.

Basically, I need to copy the manifest from the library into this, I think.

Ok, actually, the "android:installLocation="preferExternal" feature of the manifest is only support in 2.2 and higher. Hmm...

Well, first let's get this working, then we'll figure out what to do about that. Leave the target at 2.2 for now.

i wonder if in the mainifest, when it says


if it finds the app name from the import project?

Well, let's just manually fill it in for now.

Ok, there were a few other things I needed to think about.

I thought I saved it...

Well, let's see.

Nag Questions - we can take that from the library for now

App Label

Package name

And the validation code.

Ok, well, lt's just see what happens when we run this

Crash -

E/AndroidRuntime(12057): java.lang.RuntimeException: Unable to instantiate application com.kanjisoft.jlpt4.full.AppState: java.lang.ClassNotFoundException: com.kanjisoft.jlpt4.full.AppState in loader dalvik.system.PathClassLoader[/mnt/asec/com.kanjisoft.jlpt4.full-1/pkg.apk]
E

Ok, this has to do with naming the application, which I extend with an object called AppState.

I just need to give it the correct package name...


And try again. Crash...

/ActivityManager( 101): Starting: Intent { cmp=com.kanjisoft.jlpt4.full/com.jlptquiz.app.StartActivity } from pid 12126
I/LicenseChecker(12126): Binding to licensing service.
D/AndroidRuntime(12126): Shutting down VM
W/dalvikvm(12126): threadid=1: thread exiting with uncaught exception (group=0x40015560)
E/AndroidRuntime(12126): FATAL EXCEPTION: main
E/AndroidRuntime(12126): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.kanjisoft.jlpt4.full/com.jlptquiz.app.StartActivity}: java.lang.RuntimeException: native typeface cannot be made
E/AndroidRuntime(12126): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
E/AndroidRuntime(12126): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
E/AndroidRuntime(12126): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
E/AndroidRuntime(12126): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
E/AndroidRuntime(12126): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(12126): at android.os.Looper.loop(Looper.java:130)
E/


The typface is an issue? But that's include in the library. Or isn't it? Let's copy it in from

Ok, it got as far as the validation error...cool. Before I add it to the server, let's temporarily disable it in the code to see if the app works.

I have some internal checks on licensing, need to disable those...

Ok, that looks good. It's working. This was actually easier than I expected. This will make it much, much simpler to manage multiple apps. There's still a lot of work to do, but the main horror-show threat, having to constantly modify versions of the source code with each release, has been handled. Nice :)

No comments:

Post a Comment