Mobile for the masses: Sign, seal, and deliver your Android app

Add a multiple-choice quiz to your Android mobile app, then sign it with a secure digital certificate

By web logic, content is king; but for mobile users, interaction rules. Static information design is out for mobile apps, and gamification is in. This month Andrew Glover concludes his introduction to Android mobile development by adding a multiple-choice quiz feature to the example app, Overheard Word, introduced in a previous article. Then he shows you how to generate a digital signature and publish and promote your signed app on Google Play or Amazon Appstore for Android.

Share:

Andrew Glover, CTO, App47

Andrew GloverAndrew Glover is a developer and mobile technology enthusiast. He's currently the CTO of App47 and is the founder of the easyb behavior-driven development (BDD) framework. He is also the co-author of three books: Continuous Integration, Groovy in Action, and Java Testing Patterns. You can keep up with Andrew by reading his blog and by following him on Twitter.



03 September 2013

Also available in Japanese

About this series

Mobile application distribution is exploding, and so is the market for mobile development skills. This new series introduces the mobile landscape to developers who are experienced with programming but new to mobility.

Start out coding native apps in Java code, then expand your toolkit to include JVM languages, scripting frameworks, HTML5/CSS/JavaScript, third-party tools, and more. Step by step, you'll master the skills you need to meet virtually any mobile development scenario.

Read the complete series to date.

So far in this Mobile for the masses series, we've used Android as an entry point for learning how to do mobile development, including how-tos on the Android application lifecycle, implementing swipe gesture functionality in your Android apps, and working with third-party libraries to simplify development and enhance app functionality. While I'm not quite done with Android, I am feeling the itch to explore other mobile environments and techniques. So this month we'll wrap up our Android-intensive articles by adding a quiz feature to the Overheard Word demo app and preparing it for deployment in two popular app stores: Google Play and Amazon Appstore for Android. All of this will lay the foundation for what's next: a foray into mobile development with HTML5!

Gamify my app

Before we sign Overheard Word and send it off to compete with millions of other apps in the Google Play and Amazon Appstore for Android marketplace, I want to be certain that it's the best Overheard Word app that it can be. (Not familiar with our Overheard Word app? Head back to the article that introduced this demo app.) As you know, games are the current driving force of the mobile ecosystem, and even serious apps are expected to be highly interactive. Mobile apps that spark curiosity and the desire to "win" do well, even when their goal is to deliver information value. That's why Overheard Word is more than just a list of words on a page; instead, it's designed to provoke readers to go digging for vocabulary, and then reward them for learning it! (Gamification is the in-vogue terminology for that design technique, by the way.)

Free trial software

Test your Android apps with IBM Worklight

IBM Worklight Developer Edition is a free Eclipse plug-in. It provides a visual development and server platform for building, testing, and deploying native, web, and hybrid apps for Android, as well as iOS, Blackberry, and Windows Phone devices. Learn more.

Click the button to download the Worklight Developer Edition

I think we can bump up Overheard Word's stickiness (that is, its ability to capture and hold a device user's interest) with a vocabulary quiz. For this, we'll add a new Activity class to the existing program, which will present a vocabulary word definition followed by multiple words that could fit it. When a user chooses the correct word, they'll get a new definition; otherwise they'll have the opportunity to try again.

The new Activity layout will utilize RadioGroups and RadioButtons, a couple of components that we haven't played with yet. We'll also need to add TextViews for these elements. Word definitions will be based on words that the user has studied, so we'll get to re-use the Thingamajig library. I'll also take this opportunity to teach you about programming with Android Intents, Handlers, and Bundles.

Overheard Word gets quizzical

We'll start by defining a new layout to correspond with Overheard Word's quiz view, and then I'll define an Activity to display the layout. As in previous articles, I'm using Eclipse ADT (see Resources) as my development environment; I assume that you are too.

In your project, select New followed by Android XML File. Ensure that the Resource Type is Layout and that the Root Element is RelativeLayout, like I've done below.

Figure 1. Creating a new layout in Eclipse
Selecting and configuring a new layout using the Eclipse Android Development Tools

You can call your layout whatever you like. I recommend a pattern that makes it easy to quickly ascertain which app Activity the layout file is associated with.

Next, copy the XML below and paste it into your new file.

Listing 1. Quiz layout for Overheard Word
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OverheardWord" >

    <LinearLayout
        android:id="@+id/widget33"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="20dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/quiz_definition"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginLeft="13dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="48dp"
            android:text="Definition"
            android:textSize="18sp" />

        <RadioGroup
            android:id="@+id/quiz_answers"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="17dp" >

            <RadioButton
                android:id="@+id/quiz_answer_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 1" />

            <RadioButton
                android:id="@+id/quiz_answer_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 2" />

            <RadioButton
                android:id="@+id/quiz_answer_3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 3" />
        </RadioGroup>

        <TextView
            android:id="@+id/quiz_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="40dp"
            android:layout_marginTop="20dp"
            android:lines="2"
            android:text="Result"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/quiz_number"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginTop="30dp"
            android:gravity="right"
            android:text="1/10" />
    </LinearLayout>

</RelativeLayout>

Like the existing layout file for Overheard Word, which defines the study guide UI, this layout defines a series of UI elements with sample texts so that you can quickly get an idea of how things will look when your app is live. The quiz layout includes a TextView that will hold a particular definition and a RadioGroup for various words that could fit that definition. When a user selects a word, the app will be notified of an event and respond immediately, presenting either a new question or an opportunity to try again. There's also a counter for tracking a user's progress through the quiz.

Our next step is to create a new Activity class. This class should extend Activity and provide an onCreate method, as shown in Listing 2. (Note that the setContentView specifies the new layout file created in Listing 1.)

Listing 2. New Activity class: OverheardQuiz
import android.app.Activity;
import android.os.Bundle;

public class OverheardQuiz extends Activity {

 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_overheard_quiz);
 }
}

Our next step is to instantiate this new Activity, which will ultimately display Overheard Word's quiz layout. Rather than just making the quiz option a menu item, let's try using swipe gesture functionality to launch a quiz. Later, we can add the quiz to the app menu, as well.


Programming with Intents

As I've previously discussed, an Android device is extremely constrained in terms of battery life, processing power, and memory. Most personal devices also host numerous apps as well as phone and text features, so they'll have multiple activities going at once. The Android platform manages this by placing some constraints on what you, as a developer, can make your apps do at any given time. For example, you can't force an Activity class to start and display its view. Instead, you signal to the platform your intent for an Activity to start. The platform starts the Activity when it is able to.

Think of an Intent as an asynchronous mechanism to perform tasks. You suggest that the platform should do something — and it does it when it can, which, from a user's perspective, is usually immediately.

So rather than simply starting up our Activity, we need to wrap it inside an Intent. Android will then start the Activity for us. Since we're using gestures to start a new quiz Activity, we'll plug our intent into a GestureDetector instance. (See the previous two articles in this series for more about GestureDetector.) Rather than returning false In the isUpSwipe condition, we'll fire off a new Intent like so:

Listing 3. Adding the new Activity to GestureDetector
//.....
if (detector.isDownSwipe()) {
  return false;
} elseif (detector.isUpSwipe()) {
  startActivity(new Intent(getApplicationContext(), OverheardQuiz.class));
} elseif (detector.isLeftSwipe()) {
//.....

Notice that an Intent takes a Context and the class that you wish to start. startActivity is a method found on all Activity instances.

Give it a try. Fire up your emulator instance, and swipe up when it displays a word. You should be presented with the new Activity shown in Figure 2.

Figure 2. A quiz is born!
A screenshot of the initial view of the quiz layout without values

Our quiz is looking good, although we still need to add UI logic. We'll use my Thingamajig library (see Resources) for most of that logic, so we can focus on programmatically enhancing the UI.


Swipe up, swipe down

The first thing we need to do is add a gesture detector to enable a user to leave the quiz and get back to studying words. Since an up-swipe gets a user into the quiz, it makes intuitive sense that a down-swipe would exit him or her out of it. We'll call finish to quit the quiz Activity and return to study mode.

Listing 4. Calling finish inside a swipe gesture
private GestureDetector initGestureDetector() {
    return new GestureDetector(new SimpleOnGestureListener() {

      public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        try {
          final SwipeDetector detector = new SwipeDetector(e1, e2, velocityX, velocityY);
          if (detector.isDownSwipe()) {
            finish();
          } else if (detector.isUpSwipe()) {
            return false;
          } else if (detector.isLeftSwipe()) {
            return false;
          } else if (detector.isRightSwipe()) {
            return false;
          }
        } catch (Exception e) {
          // nothing
        }
        return false;
      }
    });
  }

In a previous article, we implemented an action bar inside Overheard Word's study Activity, which we used to quit the app. Here, we'll re-purpose that code to create a new quit function just for the quiz.

Listing 5. An action bar to quit the quiz
@Override
public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.overheard_quiz, menu);
  return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case R.id.quit_quiz:
    this.finish();
    return true;
  default:
    return super.onOptionsItemSelected(item);
  }
}

Note that we don't want to quit out of the app, just the quiz; so I've updated the action bar's text to "Quit Quiz" rather than just "Quit."

Words and definitions

Next, we'll set up a definition with one correct answer and two incorrect ones. Since we're using Thingamajig for the logic, all we have to do is populate the relevant fields, as shown in Listing 6.

Listing 6. Setting up a testable word with definitions
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_overheard_quiz);
    initializeGestures();
    
    List<Word> words = buildWordList();

    if (engine == null) {
      engine = WordTestEngine.getInstance(words);
    }

    final TestableWord firstWord = engine.getTestableWord();
    final TextView testDefinition = (TextView)findViewById(R.id.quiz_definition);
    testDefinition.setText(formatDefinition(firstWord.getValidDefinition()));

    final List<String> possibleAnswers = possibleAnswersFrom(firstWord);

    final int[] radios = { R.id.quiz_answer_1, R.id.quiz_answer_2, R.id.quiz_answer_3 };
    for (int x = 0; x < radios.length; x++) {
      final RadioButton rButton = (RadioButton)findViewById(radios[x]);
      rButton.setText(possibleAnswers.get(x));
    } 
}

In Listing 5, the onCreate method first builds the View based on the layout we've provided. Next, it initializes gestures. Similar to how the original study Activity worked, a WordTestEngine (rather than a WordStudyEngine) instance is created and connected to Overheard Word's store of words (in this case, a JSON file). The possibleAnswersFrom method returns a list of three definitions, of which one is correct. The WordTestEngine instance shuffles definitions so that the quiz doesn't always play the same sequence.

Finally, the for loop populates our RadioButtons with values, which are words that could match the given definition. If you fire up your app instance now, you should see a nice quiz interface with a word definition and three words to choose from.

Figure 3. The quiz UI with words and a definition
A screenshot of the updated quiz interface shows a word definition and three words to choose from

Also note the static Result button at the bottom of the screen and the counter indicating the number of quiz questions used from the ones available; in the screen capture, one question of 10 has been used.

Don't forget the code!

Visit Overheard Word in its Git repository to see the complete application source code.

We've used Thingamajig and some of our older Activity logic to make quick work of the quiz's boilerplate logic so far. Next we'll tackle the part of the quiz that we have to code by hand, which is the radio button for multiple-choice selection. Rather than have users tap an extra button (like Submit) to indicate a selection, we'll hook into the behavior of the radio group itself and listen for when an item has been selected. That way the user will get immediate feedback — no extra button taps required!


Event scheduling with Handlers

I mentioned that I'd like to provide immediate feedback to users based on their word selection. What's more, I want to program Overheard Word to briefly pause for that feedback. If a user responded correctly, I think he or she should take a moment to savor that feeling before going on to the next question. If the answer was wrong, we should let them try again or hold the screen for a second or two while displaying the correct answer. Either way, the user gets the opportunity to learn from their actions in the quiz.

But remember, we can't just do anything we'd like in our Android app; there's no forcing an app to sleep or firing up threads on demand. (It's possible to fire up a thread in an Android app, of course, but various related factors are out of our control.) Just like we use Intents to tell an Android app how we intend for it to behave, we use Handlers to indicate our desire for a delay or a concurrent action; how Android handles that request is up to Android.

Pause, please

In Android, you use Handlers for event scheduling, and also for passing messages between threads. In this case, we're going to use a Handler to schedule an event, which will be encapsulated by a Runnable instance.

In response to the event of a user selecting a word, we'll create an instance of a Handler, pass along some logic (such as displaying a new Activity) inside of a Runnable instance, and set a time delay of a few seconds.

To hook into the tap of a radio button, we'll implement an onCheckedChangeListener and attach it to an instance of the RadioGroup, as shown in Listing 7.

Listing 7. setOnCheckedChangeListener
final RadioGroup group = (RadioGroup)findViewById(R.id.quiz_answers);
    group.setOnCheckedChangeListener(new OnCheckedChangeListener() {

      @Override
      public void onCheckedChanged(final RadioGroup group, final int checkedId) {
        final RadioButton selected = (RadioButton)findViewById(checkedId);
        final String answer = (String) selected.getText();
        if (answer.equals(firstWord.getSpelling())) {
          final TextView result = (TextView)findViewById(R.id.quiz_result);
          result.setTextColor(Color.parseColor("#228b22"));
          result.setText("Correct!");
          final Handler handler = new Handler();
          handler.postDelayed(new Runnable() {
            public void run() {
              final Intent nextQuiz = new Intent(getApplicationContext(), OverheardQuiz.class);
              startActivity(nextQuiz);
              result.setText("");
              finish();
            }
          }, 2500);
        } else {
          final TextView result = (TextView)findViewById(R.id.quiz_result);
          result.setTextColor(Color.parseColor("#ff0000"));
          result.setText("Nope, that's not it! Try again.");
          final Handler handler = new Handler();
          handler.postDelayed(new Runnable() {
            public void run() {
              selected.setChecked(false);
              result.setText("");
            }
          }, 2000);
        }
      }
    });

Just like traditional GUI programming with Java AWT or Swing, you can attach listeners to events. In this case, we're attaching an OnCheckedChangeListener to the RadioGroup. When a user selects a RadioButton, the listener's code will execute. If the user's selection is correct, then a new word will be displayed in a new instance of the OverheardQuizActivity. The user also gets a friendly message rendered in green text inside the TextView, which says "Correct!".

If the user selects the wrong word, then the text result is displayed in red, and the Activity's UI is reset so that the user can try again.

Also note the two Handler instances that delay the firing of a thread. In one case, a new Intent is fired, and in another, the UI is reset.

You should also remove the default text from the quiz layout definition in Listing 1. Just go back into that XML file and remove the line android:text="Result" from the TextView whose ID is quiz_result. Now fire up your app and check it out!


Bundles in Android

The last thing we need to do is add some logic to the counter on the bottom-right corner of the quiz view (see Figure 3). As a user proceeds through the quiz, we want to show him or her their progress, as well as how many questions remain. In order for our counter to work, we need a way of keeping the state of the counter (for example, it was 2, and now it should be 3), which means we need to be able to pass data along from one activity to the next. We'll use Bundles for this task.

If you've been reading this series for a while, then you've already encountered Bundles, because a Bundle instance is the lone parameter to every Activity's onCreate method.

Bundles are essentially Maps that contain key-value pairs. You can retrieve a key's value via method calls that correspond to a value's type. In addition to being part of Activity instances, Bundles are included in Intents. In fact, if you put a key-value pair inside an Intent associated with a new Activity, you can retrieve that Bundle via the Intent that created the Activity. All of that might sound convoluted, but it will make sense once you've done it yourself.

Creating the counter

First, we update the Handler that creates a new OverheardQuizActivity, adding an incremented counter, as shown in Listing 8.

Listing 8. Adding an incremented counter
final Handler handler = new Handler();
  handler.postDelayed(new Runnable() {
    public void run() {
      final Intent nextQuiz = new Intent(getApplicationContext(), OverheardQuiz.class);
      nextQuiz.putExtra(QUIZ_NUM, ++quizNumber);
      startActivity(nextQuiz);
      result.setText("");
      finish();
    }
  }, 2500);

The putExtra method associates the key QUIZ_NUM with the incremented value of quizNumber, both of which are member variables of the OverheardQuiz class.

Next, we'll update the onCreate method of the OverheardQuiz class to obtain this value from the Intent that started it. (If onCreate doesn't find the value, it assumes it is 1. This makes sense for the first time the instance has been fired from an OverheardWordActivity, which doesn't set the QUIZ_NUM value. If the value is greater than 10, the sequence will be reset, and we'll send the user a congratulatory message (via Toast). Finally, we'll update the TextView for the counter with the new count. All of this is shown in Listing 9.

Listing 9. Updating a TextView with the current count
final Bundle previous = getIntent().getExtras();
quizNumber = (previous != null) ? previous.getInt(QUIZ_NUM) : 1;

if (quizNumber > 10) {
  quizNumber = 1;
  CharSequence text = "Great Job! You made it through 10 questions. Here's another 10!";
  Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}

final TextView quizCounter = (TextView)findViewById(R.id.quiz_number);
quizCounter.setText(quizNumber + " of 10");

Now let's fire up this final version of Overheard Word and see what it does.

Figure 4. Overheard Word is munificent!
A screenshot of the finished app showing a correct word selection and the updated counter

With that, I think we have a winning app on our hands: it offers a valuable service (vocabulary building) inside a quick, intuitive interface, and now it even has a fancy quiz feature to keep users coming back for more. The UI design could be fancier (black text on white background is pretty boring), but for now, I say it's time that we sign our app and send it off to market.


Signing your Android app

If you want to get your app on the devices of people all over the globe, you need to deploy it to a public app store. While there are many options to choose from, two of the biggest app stores are Google Play and Amazon Appstore for Android (see Resources). For security reasons, both of these stores stipulate that the apps they host must be signed.

When you sign an app, you create a certificate whose private key is in your possession. You then use this certificate to digitally sign your code. Signing is a way to establish that a particular binary was built and signed by you, and not by some unknown, potentially malicious party. Android devices will not install any app instance that isn't signed, so if you want to succeed as an Android developer, you need to know how to sign your apps.

When I showed you a few articles back how to install a test app on an Android device, you were actually signing the app with a test certificate. Test certificates are great for testing things out, but no public app store would accept one. This time you'll learn how to generate a real certificate via Eclipse.

To sign your app, go into the Eclipse ADT project menu and select Android Tools and then Export Signed Application Package. That'll bring up a wizard for generating a new certificate.

Figure 5. The Eclipse ADT certificate wizard
A screen shot of the Eclipse ADT certificate wizard

The wizard will guide you through a series of steps ending in a dialog where you will be asked to specify personal and professional details. If you've ever bought an SSL certificate for a website, then you've gone through a similar process. Note that in Android's case, however, there is no certificate authority involved in the process. App signing is thus significantly easier than getting an SSL certificate, but it does raise the possibility of fraudulent claims; there's nothing stopping me from saying that I am Bank of America or Coca-Cola, for instance.

Figure 6. Identifying information for the certificate
A screenshot of the form to input identifying information for the certificate

After you've followed the process to create a certificate and keystore (which holds your private key), you'll be able to export a digitally signed instance of your app as an .apk file, as shown in Figure 7.

Figure 7. OverheardWord.apk
A screenshot of the certificate wizard showing Overheard Word's new destination apk file name.

Once you have a digitally signed instance of your app, you are, at least technically, free to distribute it the world. There's more to distributing your app than just signing and publishing it to an app store, however. You'll also need to promote it.

Promoting your app

Public app stores provide a platform for reaching a global market, but savvy consumers in the app market won't download just any old thing. Just like you need to know how to build apps that provide good value in a functional and enjoyable package, you also have to entice users to give it a try. When you go to upload (or publish) an app on Google Play or Amazon Appstore for Android, you could be asked to do any of the following:

  • Define your app's main features
  • Describe your app in short and long formats and with tag lines
  • Provide keywords to help your app show up in searches
  • Upload screenshots of your app in various sizes
  • Provide a video of your app in action

Of course, you will also need to have determined whether you are offering your app for free or for a price; and if the app costs money, you'll need to set an appropriate, market-rate price. So truly, you might find that building and signing your app is the easy part — selling a mobile app in a booming, competitive marketplace is hard work!


Conclusion

As you know, native Android development is just one option for producing a mobile app. The next article in this series will be a first look at building a mobile web app using HTML5. Web-based mobile apps offer some advantages over native apps — like the fact that you can distribute them anywhere! But web-based apps are frequently not as polished and can't necessarily muster the same level of performance as a native app. We'll explore both the pleasures and the constraints of mobile web apps when we dive deep into HTML5.

Resources

Learn

Get products and technologies

  • Worklight: Easily create and reuse native, web, and hybrid apps for iOS, Android, Blackberry, and Windows Phone devices with IBM Worklight.
  • Overheard Word: The Github repository contains the source code of the mobile app used for demonstration in this article.
  • Thingamajig: The Github repository contains the source code of a simple word-definition engine that permits studying and quizzing word definitions.
  • Download Android: The Android SDK with the ADT bundle provides the API libraries and developer tools necessary to build, test, and debug apps for Android.

Discuss

  • Get involved in the developerWorks community: Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Mobile development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Mobile development, Java technology, Open source
ArticleID=943150
ArticleTitle=Mobile for the masses: Sign, seal, and deliver your Android app
publish-date=09032013