CS499 – Mobile Application Development Fall 2013 Programming the Android Platform Activities & Intents
Activity Provides a visual interface Typically supports one thing a user can do View an email message Show a login screen Applications can include several activities Only one activity can have ‘focus’ at a time. Other activities are paused or stopped.
Tasks Set of related activities Android manages an activity ‘back stack’ Launching an activity pushes it onto the stack Hitting the back button pops the top activity off the stack, deleting it.
Task Stack
Activity States Not started – not yet created Active Finished Resumed/Running – visible, has focus Paused – visible, does not have focus, can be terminated Stopped – not visible, can be terminated Finished
Activity Lifecycle Android communicates state changes to application by calling specific lifecycle methods: protected void onCreate() protected void onStart() protected void onResume() protected void onPause() protected void onRestart() protected void onStop() protected void onDestroy()
onCreate() protected void onCreate(Bundle savedInstanceState) Called when Activity is first being created Must be defined by the activity Setup global state: call super.onCreate() inflate UI views Configure views
Activity functions All must call super.<fn>() protected void onStart() - Called when the activity starts. protected void onRestart() - Called if the activity has been stopped and is about to be started again. Update any saved info. protected void onResume() – return to ‘focus’. Restart foreground effects
Activity functions protected void onPause() – focus is switching to another activity. Shut down foreground resources. protected void onStop() – Activity no longer visible but might be used again eventually. Can save state in case destroyed. protected void onDestroy() – removed from back stack. Save any important data and release any held resources.
public class ExampleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // The activity is being created. } @Override protected void onStart() { super.onStart(); // The activity is about to become visible. } @Override protected void onResume() { super.onResume(); // The activity has become visible (it is now "resumed"). } @Override protected void onPause() { super.onPause(); // Another activity is taking focus (this activity is about to be "paused"). } @Override protected void onStop() { super.onStop(); // The activity is no longer visible (it is now "stopped") } @Override protected void onDestroy() { super.onDestroy(); // The activity is about to be destroyed. } }
Example: Pause/Resume @Override public void onPause() { super.onPause(); // Always call the superclass method first // Release the Camera because we don't need it when paused // and other activities might need to use it. if (mCamera != null) { mCamera.release() mCamera = null; } } @Override public void onResume() { super.onResume(); // Always call the superclass method first // Get the Camera instance as the activity achieves full user focus if (mCamera == null) { initializeCamera(); // Local method to handle camera init } }
Example: Stop @Override protected void onStop() { super.onStop(); // Always call the superclass method first // Save the note's current draft, because the activity is stopping // and we want to be sure the current note progress isn't lost. ContentValues values = new ContentValues(); values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText()); values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle()); … }
Example: Start or Restart @Override protected void onStart() { super.onStart(); // Always call the superclass method first // The activity is either being restarted or started for the first time // so this is where we should make sure that GPS is enabled LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (!gpsEnabled) { // Create a dialog here that requests the user to enable GPS, and use an intent // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action // to take the user to the Settings screen to enable GPS when they click "OK" } }
Activity Lifetime Entire lifetime – from onCreate() to onDestroy() Visible lifetime – from onStart() to onStop() Foreground (focus) lifetime – from onResume() to onPause()
Configuration Changes Device configuration can change at runtime orientation, locale, etc. On configuration changes, Android typically kills & restarts the current activity Basic UI information is saved automatically. Hard to recompute data can be saved & restored explicitly.
Example: saving state static final String STATE_SCORE = "playerScore"; static final String STATE_LEVEL = "playerLevel"; ... @Override public void onSaveInstanceState(Bundle savedInstanceState) { // Save the user's current game state savedInstanceState.putInt(STATE_SCORE, mCurrentScore); savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); // Always call the superclass so it can save the view hierarchy state super.onSaveInstanceState(savedInstanceState); }
Example: restoring state @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Always call the superclass first // Check whether we're recreating a previously destroyed instance if (savedInstanceState != null) { // Restore value of members from saved state mCurrentScore = savedInstanceState.getInt(STATE_SCORE); mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); } else { // Probably initialize members with default values for a new instance } ... }
Starting New Activities Create an Intent object specifying the activity to start Can be explicit (by name) or implicit (by purpose) Pass newly created Intent to one of the following: startActivity() startActivityForResult()
The Intent Class An Intent is a data structure that specifies An operation to be performed An event that has occurred Broadcast by one component Received by 0 or more components This lecture focuses on using intents to represent operations rather than events.
Example: Explicit public class MyAppActivity extends Activity { public final static String EXTRA_MESSAGE = "cs499.examples.myapp.MESSAGE"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } /** Called when the user selects the Send button */ public void sendMessage(View view) { Intent intent = new Intent(this, DisplayMessageActivity.class); EditText editText = (EditText) findViewById(R.id.edit_message); String message = editText.getText().toString(); intent.putExtra(EXTRA_MESSAGE, message); startActivity(intent);
public class DisplayMessageActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Get the message from the intent Intent intent = getIntent(); String message = intent.getStringExtra(MyAppActivity.EXTRA_MESSAGE); // Create the text view TextView textView = new TextView(this); textView.setTextSize(40); textView.setText(message); setContentView(textView); } // when this activity finishes, it is popped from the stack
<. xml version="1. 0" encoding="utf-8" <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cs499.examples.myapp" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MyAppActivity" <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".DisplayMessageActivity" /> </application> </manifest>
Getting Results from Activities startActivityForResult() Implement onActivityResult() callback method When the activity is done, it will return the result in an intent to your callback onActivityResult() – handles this return event Note: If the current activity can start multiple activities returning values, the returns are all handled with a single onActivityResult() method.
Example: getting return values static final int PICK_CONTACT_REQUEST = 1; // The request code ... private void pickContact() { Intent pickContactIntent = new Intent(Intent.ACTION_PICK, new Uri("content://contacts")); pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST); }
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Check which request we're responding to if (requestCode == PICK_CONTACT_REQUEST) { // Make sure the request was successful if (resultCode == RESULT_OK) { // The user picked a contact. // The Intent's data Uri identifies which contact was selected. // Do something with the contact here } } }
More complete Example Main activity – gets two integers and sends these to the second activity on a button press. When data arrives back, print out this Second activity – started by another activity, receives two strings that represent integers. On a button press, adds the integers and returns the result.
AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cs499.examples.intentexample" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".IntentExampleActivity" <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ActivityA" /> </application> </manifest>
main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="First Activity" /> <EditText android:layout_width="fill_parent“ android:layout_height="wrap_content“ android:hint="Enter an integer" android:id="@+id/input1” /> <EditText android:layout_width="fill_parent“ android:layout_height="wrap_content“ android:hint="Enter an integer“ android:id="@+id/input2” /> <Button android:layout_width="fill_parent“ android:layout_height="wrap_content" android:text="Update int“ android:onClick="sendMessage“ /> <TextView android:layout_width="fill_parent“ android:layout_height="wrap_content" android:id="@+id/result” /> </LinearLayout>
Step 1 – create Main Activity package cs499.examples.intentexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class IntentExampleActivity extends Activity { // parameters for the new activity public final int ACTIVITY_RESULT = 1; private TextView mResultText; private EditText mVal1, mVal2; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mResultText=(TextView)findViewById(R.id.result); mVal1 =(EditText)findViewById(R.id.input1); mVal2=(EditText)findViewById(R.id.input2); } public void sendMessage(View view) { // Create an intent to send values in response to button Intent intent = new Intent(this, ActivityA.class); String s1 = mVal1.getText().toString(); if (s1.length() != 0) intent.putExtra(ActivityA.PARAM1, s1); else intent.putExtra(ActivityA.PARAM1,"0"); String s2 = mVal2.getText().toString(); if (s2.length() != 0) intent.putExtra(ActivityA.PARAM2, s2); else intent.putExtra(ActivityA.PARAM2,"0"); startActivityForResult(intent,ACTIVITY_RESULT); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == ACTIVITY_RESULT) { String address = data.getExtras(). getString(ActivityA.RETVAL); mResultText.setText(address);
Step 2 – User presses button in Main Activity package cs499.examples.intentexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class IntentExampleActivity extends Activity { // parameters for the new activity public final int ACTIVITY_RESULT = 1; private TextView mResultText; private EditText mVal1, mVal2; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mResultText=(TextView)findViewById(R.id.result); mVal1 =(EditText)findViewById(R.id.input1); mVal2=(EditText)findViewById(R.id.input2); } public void sendMessage(View view) { // Create an intent to send values in response to button Intent intent = new Intent(this, ActivityA.class); String s1 = mVal1.getText().toString(); if (s1.length() != 0) intent.putExtra(ActivityA.PARAM1, s1); else intent.putExtra(ActivityA.PARAM1,"0"); String s2 = mVal2.getText().toString(); if (s2.length() != 0) intent.putExtra(ActivityA.PARAM2, s2); else intent.putExtra(ActivityA.PARAM2,"0"); startActivityForResult(intent,ACTIVITY_RESULT); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == ACTIVITY_RESULT) { String address = data.getExtras(). getString(ActivityA.RETVAL); mResultText.setText(address);
Step 3 - Second Activity starts package cs499.examples.intentexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class ActivityA extends Activity { public final static String RETVAL = "cs499.examples.activitya.RETVAL"; public final static String PARAM1 = "cs499.examples.activitya.P1"; public final static String PARAM2 = "cs499.examples.activitya.P2"; private TextView mInt1, mInt2; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get parameters from the calling activity Intent intent = getIntent(); String str1 = intent.getStringExtra(IntentExampleActivity. PARAM1); String str2 = PARAM2); // Create the text view using the input values setContentView(R.layout.main_c); mInt1 = (TextView) findViewById(R.id.start1); mInt1.setText(str1); mInt2 = (TextView) findViewById(R.id.start2); mInt2.setText(str2); } public void sendMessage(View view) { // Do something in response to button int v1 = Integer.parseInt(mInt1.getText().toString()); int v2 = Integer.parseInt(mInt2.getText().toString()); String result = Integer.toString(v1+v2); Intent intent = new Intent(); intent.putExtra(STRVAL,result ); // Set result and finish this Activity setResult(Activity.RESULT_OK, intent); finish(); }
main_c.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent” android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent” android:layout_height="wrap_content" android:text="Second Activity" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/start1" /> <TextView android:layout_width="fill_parent” android:layout_height="wrap_content" android:id="@+id/start2" /> <Button android:layout_width="fill_parent“ android:layout_height="wrap_content" android:text="Return Result“ android:onClick="sendMessage“ /> </LinearLayout>
Step 4 – button press in Second Activity package cs499.examples.intentexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class ActivityA extends Activity { public final static String RETVAL = "cs499.examples.activitya.RETVAL"; public final static String PARAM1 = "cs499.examples.activitya.P1"; public final static String PARAM2 = "cs499.examples.activitya.P2"; private TextView mInt1, mInt2; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get parameters from the calling activity Intent intent = getIntent(); String str1 = intent.getStringExtra(IntentExampleActivity. PARAM1); String str2 = PARAM2); // Create the text view using the input values setContentView(R.layout.main_c); mInt1 = (TextView) findViewById(R.id.start1); mInt1.setText(str1); mInt2 = (TextView) findViewById(R.id.start2); mInt2.setText(str2); } public void sendMessage(View view) { // Do something in response to button int v1 = Integer.parseInt(mInt1.getText().toString()); int v2 = Integer.parseInt(mInt2.getText().toString()); String result = Integer.toString(v1+v2); Intent intent = new Intent(); intent.putExtra(STRVAL,result ); // Set result and finish this Activity setResult(Activity.RESULT_OK, intent); finish(); }
Step 5 – return to Main Activity package cs499.examples.intentexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class IntentExampleActivity extends Activity { // parameters for the new activity public final int ACTIVITY_RESULT = 1; private TextView mResultText; private EditText mVal1, mVal2; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mResultText=(TextView)findViewById(R.id.result); mVal1 =(EditText)findViewById(R.id.input1); mVal2=(EditText)findViewById(R.id.input2); } public void sendMessage(View view) { // Create an intent to send values in response to button Intent intent = new Intent(this, ActivityA.class); String s1 = mVal1.getText().toString(); if (s1.length() != 0) intent.putExtra(ActivityA.PARAM1, s1); else intent.putExtra(ActivityA.PARAM1,"0"); String s2 = mVal2.getText().toString(); if (s2.length() != 0) intent.putExtra(ActivityA.PARAM2, s2); else intent.putExtra(ActivityA.PARAM2,"0"); startActivityForResult(intent,ACTIVITY_RESULT); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == ACTIVITY_RESULT) { String address = data.getExtras(). getString(ActivityA.RETVAL); mResultText.setText(address);
Implicit Intents Often used to use components from existing applications: Uri number = Uri.parse("tel:5551234"); Intent callIntent = new Intent(Intent.ACTION_DIAL, number); Uri webpage = Uri.parse("http://www.android.com"); Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage); Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI); Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30); Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30); calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis()); calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis()); calendarIntent.putExtra(Events.TITLE, "Ninja class"); calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");
Intent Resolution When the activity to be activated is not named, the system attempts to find activities that match the intent Only matches on Action Data (both URI and mime data type) Category
Intent Actions String representing the operation Examples: new Intent(Intent.ACTION_VIEW) Intent newInt = new Intent(); newInt.setAction(Intent.ACTION_VIEW);
Intent Category Additional information about the components to handle the intent Examples:
Intent Data Data associated with the Intent Examples: Formatted as a Uniform Resource Identifier (URI) Examples: Data to view on a map geo:0,0?q=1600+Pennsylvania+Ave+Washington+DC Number to dial on the phone tel:+15555555555
Intent Data Setting the Intent Data new Intent(Intent.ACTION_CALL, Uri.parse(tel:+15555555555)); Intent newInt = new Intent(Intent.ACTION_CALL); newInt.setData(Uri.parse(tel:+15555555555));
Additional information associated with the intent Treated as a map (key-value pairs) Setting the Extra attribute Several forms depending on data type putExtra(String name, String value): putExtra(String name, float[] value); …
Intent Type Sets MIME type of the intent data for example, “image/*” If unspecified, Android will infer the type Intent.setType(String type) Intent.setDataAndType(Uri data, String type)
Implicit Intents Usually necessary to verify that there is at least one component capable of handling the intent: Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); PackageManager packageManager = getPackageManager(); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); boolean isIntentSafe = activities.size() > 0; if (isIntentSafe) startActivity(mapIntent);