Assignment 3

Welcome to week three of Mobile Systems and Applications! (cs.upt.ro/~alext/msa/lab3)

Topics of discussion

  • The back stack and the activity lifecycle.

  • Configurations in the manifest file, screen orientation.

  • Working with intent filters and creating custom filters.

The Back stack

An application usually contains multiple activities. Each activity should be designed around a specific kind of action the user can perform and can start other activities. An activity can even start activities that exist in other applications on the device. Even though the activities may be from different applications, Android maintains this seamless user experience by keeping both activities in the same task. A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.

The device Home screen is the starting place for most tasks. When the user touches an icon in the application launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no task exists for the application (the application has not been used recently), then a new task is created and the "main" activity for that application opens as the root activity in the stack.

When the current activity starts another, the new activity is pushed on the top of the stack and takes focus. The previous activity remains in the stack, but is stopped. When an activity stops, the system retains the current state of its user interface. When the user presses the Back button, the current activity is popped from the top of the stack (the activity is destroyed) and the previous activity resumes (the previous state of its UI is restored). Activities in the stack are never rearranged, only pushed and popped from the stack—pushed onto the stack when started by the current activity and popped off when the user leaves it using the Back button. As such, the back stack operates as a "last in, first out" object structure. Figure 1 offers and example of the foreground and background activities on the stack.

Fig.1.

Activity lifecycle

As a user navigates through, out of, and back to your app, the Activity instances in your app transition between different states in their lifecycle. For instance, when your activity starts for the first time, it comes to the foreground of the system and receives user focus. During this process, the Android system calls a series of lifecycle methods on the activity in which you set up the user interface and other components. If the user performs an action that starts another activity or switches to another app, the system calls another set of lifecycle methods on your activity as it moves into the background (where the activity is no longer visible, but the instance and its state remains intact).

Looking at the lifecycle methods depicted in Figure 2, there are three lessons to be learned when managing an activity:

  1. Starting an activity - Read online about the lifecycle callbacks, specifying a launcher and when an activity is destroyed.

  2. Pausing and resuming an activity - When an activity looses focus it becomes paused. For example, when switching between apps, only one of the apps has the focus at any time; the system pauses all other apps. Similarly, when a semi-transparent activity opens (such as a dialog), the previous activity pauses. As long as the activity is still partially visible but currently not the activity in focus, it remains paused. Read more online.

  3. Stopping and restarting an activity - Read online how properly stopping and restarting your activity is an important process in the activity lifecycle that ensures your users perceive that your app is always alive and doesn't lose their progress.

Fig. 2.

Programatically, we may override any of the method calls depicted in Figure 2. By default, we write new code in an activitiy's onCreate method. However, all other methods may be implemented a needed. Of course, in all cases, do no forget to pass the call to the super class.

Let's pick an example of navigating through an app with 2 activities: ActivityA and ActivityB, and understand which lifecycle methods are called and when.

  • Say we enter the app by launching activity A. In this case, methods onCreate, onStart, on Resume from A are called.

          • Launch app and start A

                • 10-07 11:02:55.581 14421-14421/com.topindustries.apps.msa D/Lifecycle: onCreate A

                • 10-07 11:02:55.581 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStart A

                • 10-07 11:02:55.581 14421-14421/com.topindustries.apps.msa D/Lifecycle: onResume A

  • Next, we start activity B from A. Methods onPause (A), onCreate (B), onStart (B), onResume (B), onStop(A) wil be called in this order. Notice that A is first paused, but stopped only at the end. This is both a safety and performance consideration. After hiding A, B should come up on the screen as fast as possible, so that the user does not notice the transition. After B is initialized and running, A will be stopped. Also, if something goes wrong with starting B, A will still be in a paused state, meaning that it can be resumed quickly.

          • Start B from A

                • 10-07 11:03:31.302 14421-14421/com.topindustries.apps.msa D/Lifecycle: onPause A

                • 10-07 11:03:31.352 14421-14421/com.topindustries.apps.msa D/Lifecycle: onCreate B

                • 10-07 11:03:31.352 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStart B

                • 10-07 11:03:31.352 14421-14421/com.topindustries.apps.msa D/Lifecycle: onResume B

                • 10-07 11:03:31.782 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStop A

  • Next we start activity A from B. Yes, we can start a new A. Thus, methods onPause (B), onCreate (A), onStart (A), onResume (A), onStop(B) wil be called in this order. Remember, this a is another A than the first one which is still stopped.

          • Start a new A from B

                • 10-07 11:04:18.692 14421-14421/com.topindustries.apps.msa D/Lifecycle: onPause B

                • 10-07 11:04:18.752 14421-14421/com.topindustries.apps.msa D/Lifecycle: onCreate A

                • 10-07 11:04:18.752 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStart A

                • 10-07 11:04:18.752 14421-14421/com.topindustries.apps.msa D/Lifecycle: onResume A

                • 10-07 11:04:19.212 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStop B

  • Now we click the back button, so we return to activity B. In this case, the second A will be discarded completely. Methods onPause (A), onRestart (B), onStart (B), onResume (B), onStop (A), onDestroy (A) will be called. Notice that B is restarted and A is finally stopped and destroyed.

          • Go back to B

                • 10-07 11:05:12.093 14421-14421/com.topindustries.apps.msa D/Lifecycle: onPause A

                • 10-07 11:05:12.093 14421-14421/com.topindustries.apps.msa D/Lifecycle: onRestart B

                • 10-07 11:05:12.093 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStart B

                • 10-07 11:05:12.093 14421-14421/com.topindustries.apps.msa D/Lifecycle: onResume B

                • 10-07 11:05:12.723 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStop A

                • 10-07 11:05:12.723 14421-14421/com.topindustries.apps.msa D/Lifecycle: onDestroy A

  • Further, we click the back button again, and we return from B to the original activity A. The same order of methods will be called as above, resulting in the destruction of B.

          • Go back to A

                • 10-07 11:05:38.954 14421-14421/com.topindustries.apps.msa D/Lifecycle: onPause B

                • 10-07 11:05:38.964 14421-14421/com.topindustries.apps.msa D/Lifecycle: onRestart A

                • 10-07 11:05:38.964 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStart A

                • 10-07 11:05:38.964 14421-14421/com.topindustries.apps.msa D/Lifecycle: onResume A

                • 10-07 11:05:39.634 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStop B

                • 10-07 11:05:39.634 14421-14421/com.topindustries.apps.msa D/Lifecycle: onDestroy B

    • Finally, we click the back button and arrive on the Home screen, having closed all activities of the app.

            • Go back to Home screen

                  • 10-07 11:06:37.835 14421-14421/com.topindustries.apps.msa D/Lifecycle: onPause A

                  • 10-07 11:06:40.385 14421-14421/com.topindustries.apps.msa D/Lifecycle: onStop A

                  • 10-07 11:06:40.385 14421-14421/com.topindustries.apps.msa D/Lifecycle: onDestroy A

Runtime output with Logcat

The classic System.out.println from java applications is replaced by Logcat in Android Studio. Logcat is a command-line tool that dumps a log of system messages, including stack traces when the device throws an error and messages that you have written from your app with the Log class. You may call Log anywhere in a project with any of the following line of codes:

Log.d(ActivityA.TAG, "A debug message");

Log.v(ActivityA.TAG, "A verbose message");

Log.i(ActivityA.TAG, "Some info");

Log.e(ActivityA.TAG, "An error occurred");

Log.w(ActivityA.TAG, "An example warning");

There are various channels in which to write the log message, like debug verbose, info, error, warning. These channels help you filter out the various messages you might have in an app at runtime. The static call to any of these methods (d,e...) needs a string TAG and the message that will be printed in logcat. The TAG is usually a public/private static final String that holds the activity name, or any unique app identifier. To view the messages in Android studio you must open the logcat tag from the Android Monitor tab found on the bottom menu bar of the IDE. There you may select the channel type (verbose prints all messages) and write your own filter for messages. In the example in Figure 3, we use the filter text "lifecycle" (which is the tag used) and see the five messages that were defined above.

Fig.3.

Do not use toasts for debugging purposes. All of the log messages you add to your code will not be visible for the user of the application, whereas you may forget to comment/remove some debugging toasts. Also, working with logcat is much more versatile than having short lasting popups appear on your phone. More about logcat here.

Task #1

  • In your existing HelloAndroid project, add a new java package named lifecycle, and create three new empty activities: ActivityA, ActivityB, and ActivityC. Each of them should offer the user three buttons to navigate to any of the activities, including itself. See the following figure and code snippets as a suggestion for layout and button-click handling:

              • <Button

              • android:id="@+id/buttonA"

              • android:layout_width="wrap_content"

              • android:layout_height="wrap_content"

              • android:onClick="clicked"

              • android:text="Start A"/>

              • <Button

              • android:id="@+id/buttonB"

              • android:layout_width="wrap_content"

              • android:layout_height="wrap_content"

              • android:onClick="clicked"

              • android:text="Start B"/>

              • <Button

              • android:id="@+id/buttonC"

              • android:layout_width="wrap_content"

              • android:layout_height="wrap_content"

              • android:onClick="clicked"

              • android:text="Start C"/>

public void clicked(View view) {

switch (view.getId()) {

case R.id.buttonA:

startActivity(new Intent(this, ActivityA.class));

break;

case R.id.buttonB:

startActivity(new Intent(this, ActivityB.class));

break;

case R.id.buttonC:

startActivity(new Intent(this, ActivityC.class));

break;

}

}

  • Override all lifecycle methods in all three activities and add logcat messages like Log.d(TAG, "onCreate A") - representing the message of the onCreate method in activity A. Do the same for all other methods in all three classes. You should end up with 3x7=21 logs.

                • @Override

                • protected void onCreate(Bundle savedInstanceState) {

                • super.onCreate(savedInstanceState);

                • setContentView(R.layout.activity_a);

                • setTitle("A");

                • Log.d(TAG, "onCreate A");

                • }

                  • @Override

                  • protected void onResume() {

                  • super.onResume();

                  • Log.d(ActivityA.TAG, "onResume A");

                  • }

                  • //....

  • Navigate through the app in multiple scenarios and carefully follow the sequence of lifecycle calls. Also use the back and home buttons in the sequence of commands. For example, try start A - B - C - A - back - home - relaunch app - back - back. How does the back stack look like during these calls?

Intent filters

An Android Intent is an abstract description of an operation to be performed. It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested BroadcastReceiver components, and startService or bindService to communicate with a background Service. The intent itself, an Intent object, is a passive data structure holding an abstract description of an operation to be performed.

You have already seen how an Intent may call another activity. Android OS uses filters to pinpoint the set of Activities, Services, and Broadcast receivers that can handle the Intent with help of specified set of action, categories, data scheme associated with an Intent. You will use <intent-filter> element in the manifest file to list down actions, categories and data types associated with any activity, service, or broadcast receiver.

Below is an example of the action MAIN intent filter that must be present in the launcher activity of an app. When the users taps on the home screen icon of the app, the activity defining this filter will be the one starting as the main (or launcher) activity.

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

We may define custom filters that will be intercepted by our app activities, and may be launched by any other app on the phone. Here is an example of one such filter which intercepts the action VIEW and may process http requests:

<intent-filter>

<action android:name="android.intent.action.VIEW"/>

<action android:name="myfilter.LAUNCH"/>

<category android:name="android.intent.category.DEFAULT"/>

<data android:scheme="http"/>

</intent-filter>

Refer to this online tutorial for full examples.

Task #2

  • Create a new package named *.intents and add two new activities inside: MainIntentActivity and IntentFilterActivity. Set MainIntentActivity as the new launcher activity of the app.

  • In the manifest file, add a custom intent filter that responds to action VIEW (like in the example above) and also to a custom action (let's call it <action android:name="MSA.LAUNCH"/>). It should be compatible with data schemes starting with http and https. See tutorial for hints on data scheme.

  • Add four buttons in MainIntentActivity, each starting an action using an implicit intent.

      • The first button should create an action VIEW intent to view Uri.parse("http://www.google.com")

      • The second button should create an action VIEW intent to view Uri.parse("tel:00401213456")

      • The third button should create an action MSA.LAUNCH intent to view Uri.parse("https://www.google.com")

      • The fourth button should create an action MSA.LAUNCH intent to view Uri.parse("tel:00401213456")

  • When the intent is captured by your IntentFilterActivity, show the intent data in the activity. Use this short code to do so, and ensure you define a TextView named textview in your layout.

  • Explain the behavior when pressing the four buttons.

                • public class IntentFilterActivity extends Activity {

                • @Override

                • protected void onCreate(Bundle savedInstanceState) {

                • super.onCreate(savedInstanceState);

                • setContentView(R.layout.activity_intent_filter);

                • TextView textView = (TextView) findViewById(R.id.textView);

                • Uri url = getIntent().getData();

                • textView.setText(url.toString());

                • }

                • }

Have a fruitful week.

Durian fruit

Durian is a big fruit, which is spiky and green colour, native to Malaysia and Indonesia. In fact, it is considered as "King of the Fruit" throughout the South East Asia. It has a creamy surface, and the taste of its flesh takes eaters into ecstasies. It has an extremely nasty odor described as garlic like, similar to stinky feet, and like Limburger cheese. Some countries even veto the presence of durian in hotels and on public transportation due to its nasty smell.

http://www.fruitsinfo.com/Durian-Exotic-fruits.php