Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Mobile Application Frameworks and Services Lecture: Android Applications and Data Management Dr. Panayiotis Alefragis Professor of Applications Masters Science Program: Technologies and Infrastructures for Broadband Applications and Services 1 Anatomy of Android Applications Core Components Core components are the primordial classes or building blocks from which apps are made. An Android application consists of one or more core component objects. Components work in a cooperative mode, each contributing somehow to the completion of the tasks undertaken by the app. Each core component provides a particular type of functionality and has a distinct lifecycle. A lifecycle defines how the component is created, transitioned, and destroyed. There are four type of core components 1. An Activity 2. A Service 3. A broadcast receiver 4. A content provider Android's Core Components 1. Activity Class • An Activity object is similar to a WindowsForm. It usually presents a single graphical visual interface (GUI) which in addition to the displaying/coMecting of data, provides some kind of 'code-behind' functionality. • A typical Android application contains one or more Activity objects. • Applications must designate one activity as their main task or entry point. That activty is the first to be executed when the app is launched. • An activity may transfer control and data to another activity through an interprocess communication protocol called intents. • For example, a login activity may show a screen to enter user name and password. After clicking a button some authentication process is applied on the data, and before the login activity ends some other activity is called. Android's Core Components 2. Service Class • Services are a special type activity that do not have a visual user interface. A service object may be active without the user noticing its presence. • Services are analogous to secondary threads, usually running some kind of background 'busy-work' for an indefinite period of time. • Applications start their own services or connect to services already active. • Examples: Your background GPS service could be set to quietly run in the backgroud detecting location information from satellites, phone towers or wi-fi routers. The service could periodically broadcast location coordinates to any app listening for that kind of data. An application may opt for binding to the running GPS service and use the data that it supplies. 3-5 Android's Core Components 2. Service Background Foreground PANDORA GPS In this example a music service (say Pandora Radio) and GPS location run in the background. The selected music station is heard while other GUIs are show on the device's screen. For instance, our user -an avid golfer- may switch between occasional golf course data reading (using the GolfShot app) and "Angry Birds" (some of his playing partners could be very slow). Android's Core Components 3. Broadcast Receiver Class • A BroadcastReceiver is a dedicated listener that waits for a triggering system-wide message to do some work. The message could be something like: low-battery, wi-fi connection available, earth-quakes in California, speedcamera nearby. • Broadcast receivers do not display a user interface. • They tipically register with the system by means of a filter acting as a key. When the broadcasted message matches the key the receiver is activated. • A broadcast receiver could respond by either executing a specific activity or use the notification mechanism to request the user's attention. 3. Broadcast Receiver Broadcast Receiver Foreground Activity Method() Waiting. My filter Work to be done after receiving an ORANGE message only accepts ORANGE signals. Ignoring all others. 3-8 Android's Core Components 4. Content Provider Class • A content provider is a data-centric service that makes persistent datasets available to any number of applications. • Common global datasets include: contacts, pictures, messages, audio files, emails. • The global datasets are usually stored in a SQLite database (however the developer does not need to be an SQL expert) • The content provider class offers a standard set of parametric methods to enable other applications to retrieve, delete, update, and insert data items. 3-9 4. Content Provider Class User Application query(...) \r « insert(...) «delete(...) V > I update(...) A Content Provider is a wrapper that hides the actual physical data. Users interact with their data through a common object interface. 3 - 10 Component's Life Cycle Life and Death in Android • Each Android application runs inside its own instance of a Dalvik Virtual Machine (DVM). • At any point in time several parallel DVM instances could be active (real parallelism as opposed to task-switching) • Unlike a common Windows or Unix process, an Android application does not completely control the completion of its lifecycle. Occasionally hardware resources may become critically low and the OS could order early termination of any process. The decision considers factors such as: 1. Number and age of the application's components currently running, 2. relative importance of those components to the user, and 3. how much free memory is available in the system. Component's Life Cycle Life and Death in Android All components execute according to a master plan that consists of: 1. A beginning - responding to a request to instantiate them 2. An end - when the instances are destroyed. 3. A sequence of in-between states - components sometimes are active or inactive, or in the case of activities - visible or invisible. Life as an Android Application: Start Active / Inactive Visible / Invisible End Component's Life Cycle The Activity Stack • Activities in the system are scheduled using an activity stack. • When a new activity is started, it is placed on top of the stack to become the running activity The previous activity is pushed-down one level in the stack, and may come back to the foreground once the new activity finishes. If the user presses the Back Button WBM the current activity is terminated and the previous activity on the stack moves up to become active. Android 4.0 introduced the 'Recent app' button to arbitrarily pick as 'next' any entry currently in the stack (more on this issue later) Virtual buttons (Android 4.x): Back, Home, Recent apps Component's Life Cycle 3 - 14 Component's Life Cycle Life Cycle Callbacks When progressing from one state to the other, the OS notifies the application of the changes by issuing calls to the following protected transition methods: void onCreate( ) void onStart( ) void onRestart( ) void onResume( ) void onPause( ) void onStop( ) void onDestroy( ) Life Cycle Callbacks Most of your code goes here -> Save your important data here 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. } } Reference: http://developer.android.com/reference/android/app/Activity.html 3 - 16 Life Cycle: Activity States and Callback Methods An activity has essentially three phases: 1. It is active or running 2. It is paused or 3. It is stopped . Moving from one state to the other is accomplished by means of the callback methods listed on the edges of the diagram. 3 - 17 Component's Life Cycle Activity State: RUNNING 1. It is active or running when it is in the foreground of the screen (at the top of the activity stack). This is the activity that has "focus" and its graphical interface is responsive to the user's interactions. 3 - 18 Component's Life Cycle Activity State: PAUSED 2. It is paused if it has lost focus but is still visible to the user. That is, another activity seats on top of it and that new activity either is transparent or doesn't cover the full screen. A paused activity is alive (maintaining its state information and attachment to the window manager). Paused activities can be killed by the system when available memory becomes extremely low. 3 - 19 Component's Life Cycle Activity State: STOPPED 3. It is stopped if it is completely obscured by another activity. Continues to retain all its state information. It is no longer visible to the user ( its window is hidden and its life cycle could be terminated at any point by the system if the resources that it holds are needed elsewhere). 3 - 20 Activity Life Cycle Reference: http://developer.android.com/training/basics/activitv-lifecvcle/starting.html Application's Life Cycle Foreground Lifetime • An activity begins its lifecycle when it enters the onCreate() state. • If it is not interrupted or dismissed, the activity performs its job and finally terminates and releases resources when reaching the onDestroy() event. 3 - 27 Application's Life Cycle Associating Lifecycle Events with Application's Code Applications do not need to implement each of the transition methods, however there are mandatory and recommended states to consider (Mandatory) onCreate() must be implemented by each activity to do its initial setup. The method is executed only once on the activity's lifetime. (Highly Recommended) onPause() should be implemented whenever the application has some important data to be committed so it could be reused. 3 - 28 Application's Life Cycle Associating Lifecycle Events with Application's Code onCreate() • This is the first callback method to be executed when an activity is created. • Most of your application's code is written here. • Typically used to initialize the application's data structures, wire-up UI view elements (buttons, text boxes, lists) with local Java controls, define listeners' behavior, etc. • It may receive a data Bundle object containing the activity’s previous state (if any). Followed by onStart() —— onResume().... 3 - 29 Application's Life Cycle Associating Lifecycle Events with Application's Code onPause() 1. Called when the system is about to transfer control to another activity. It should be used to safely write uncommitted data and stop any work in progress. 2. The next activity waits until completion of this state. 3. Followed either by onResume() if the activity returns back to the foreground, or by onStop() if it becomes invisible to the user. 4. A paused activity could be killed by the system. 3 - 30 Application's Life Cycle Killable States • Android OS may terminate a killable app whenever the resources needed to run other operation of higher importance are critically low. • When an activity reaches the methods: onPause(), onStop(), and onDestroy()it becomes killable. • onPause() is the only state that is guaranteed to be given a chance to complete before the process is terminated. • You should use onPause()to write any pending persistent data. 3 - 31 Application's Life Cycle Data Persistence using Android SharedPreferences Class • SharedPreferences is a simple Android persistence mechanism used to store and retrieve <key,value> pairs, where key is a string and value is a primitive data type (int, float, string...). • This container class reproduces the structure and behavior of a Java HashMap, however; unlike HashMaps it is persistent. • Appropriate for storing small amounts of state data across sessions. SharedPreferences myPrefSettings = getSharedPreferences(MyPreferrenceFile, actMode); Persistence is an important concept in Android, and it is discussed in more detail latter. 3 - 32 Application's Life Cycle Data Persistence using Android SharedPreferences Class SharedPreference files are permanently stored in the application's process space. Use DDMS file explorer to locate the entry: data/data/your-package-name/sharedprefs f ^Threads @ Heap @ Allocation Tracker •?* Netwc Name Size Da- acct 2D1 t> Qii!? cache 201 t> & config 201 g] 201 a data ^ 201 tH? dTir 201 > & app 201 & app-asec 201 & app-lib 201 & app-private 201 & backup 201 bug reports 201 [_ Hnluj^cache 201 A I23> data 201 TS com.android.backupconfirm 201 Key Value K l^a, mm snHrniH KmiA/cpr .A csu.matoE.lifecycle 2013-09-06 11:35 drwxr-x--:; t> Q3>cache 2013-09-05 14:17 drwxrwx--x & lib 2013-09-06 11:35 Irwxrwxrwx -> /data/a.. a £57 shared_prefs 2013-09-06 11:54 drwxrwx:--x 130 2013-09-06 11:54 -rw-rw— ^ 2013-09-03 13:39 drwxr-K--K 2013-09-03 ini3_no_n3 13::36 dn/irar-K--rl m/vni/v. _ _ ^ U) myPrefFilel.xm.l ■ & jp.co.omronsott.openwnn [> 25? dontpanic K. 2zi_ rJ rmn ) 1 3 - 33 Application's Life Cycle A complete Example: The LifeCycle App The following application demonstrates the transitioning of a simple activity through the Android's sequence of Life-Cycle states. 1. A Toast-msg will be displayed showing the current event's name. 2. An EditText box is provided for the user to indicate a background color. 3. When the activity is paused the selected background color value is saved to a SharedPreferences container. 4. When the application is re-executed the last choice of background color should be applied. 5. An EXIT button should be provide to terminate the app. 6. You are asked to observe the sequence of messages displayed when the application: 1. Loads for the first time 2. Is paused after clicking HOME button 3. Is re-executed from launch-pad 4. Is terminated by pressing BACK and its own EXIT button 5. Re-executed after a background color is set -3 - 34 Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tooLs" android:id="@+id/myScreenl" android:layout_width="fiLL_parent" android:layout_height="fiLL_parent" android:orientation="verticaL" tools:context=".MainActivity" > <EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Pick background (red, green, blue, white) 1 android:ems="10" > <requestFocus /> </EditText> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Exit" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" spy box - try clicking HOME and BACK" /> </LinearLayout> 3 - 35 Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.2 package csu.matos.lifecycle; import java.util.Locale; . . . //other libraries omitted for brevity public class MainActivity extends Activity { //class variables private Context context; private int duration = Toast.LENGTH_SHORT; //PLUMBING: Pairing GUI controls with Java objects private Button btnExit; private EditText txtColorSelected; private TextView txtSpyBox; private LinearLayout myScreen; private String PREFNAME = "myPrefFile1"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //display the main screen setContentView(R.layout.activity_main); //wiring GUI controls and matching Java objects txtColorSelected = (EditText)findViewById(R.id.editTextl); btnExit = (Button) findViewById(R.id.buttonl); txtSpyBox = (TextView)findViewById(R.id.textViewl); myScreen = (LinearLayout)findViewById(R.id.myScreenl); 3 - 36 Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.3 //set GUI listeners, watchers,... btnExit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } }); / /observe (text) changes made to EditText box (color selection) txtColorSelected.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // nothing TODO, needed by interface } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // nothing TODO, needed by interface } @Override public void afterTextChanged(Editable s) { //set background to selected color String chosenColor = s.toString().toLowerCase(Locale.US); txtSpyBox.setText(chosenColor); setBackgroundColor(chosenColor, myScreen); } }); 3 - 37 Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.4 //show the current state's name context = getApplicationContext(); Toast.makeText(context, "onCreate", duration).show(); } //onCreate @Override protected void onDestroy() { super.onDestroy(); } Toast.makeText(context, "onDestroy", duration).show(); @Override protected void onPause() { super.onPause(); //save state data (background color) for future use } String chosenColor = txtSpyBox.getText().toString(); saveStateData(chosenColor); Toast.makeText(context, "onPause", duration).show(); @Override protected void onRestart() { super.onRestart(); } Toast.makeText(context, "onRestart", duration).show(); Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.5 @Override protected void onResume() { super.onResume(); Toast.makeText(context, "onResume", duration).show(); } @Override protected void onStart() { super.onStart(); //if appropriate, change background color to chosen value updateMeUsingSavedStateData(); Toast.makeText(context, "onStart", duration).show(); } @Override protected void onStop() { super.onStop(); Toast.makeText(context, "onStop", duration).show(); } Example: The LifeCycle App – Code MainActivity.java pp.6 private void setBackgroundColor(String chosenColor, LinearLayout myScreen) { //hex color codes: 0xAARRGGBB AA:transp, RR red, GG green, BB blue if (chosenColor.contains("red")) myScreen.setBackgroundColor(0xffff0000); //Color.RED if (chosenColor.contains("green")) myScreen.setBackgroundColor(0xff00ff00); //Color.GREEN if (chosenColor.contains("blue")) myScreen.setBackgroundColor(0xff0000ff); //Color.BLUE if (chosenColor.contains("white")) myScreen.setBackgroundColor(0xffffffff); //Color.BLUE } //setBackgroundColor private void saveStateData(String chosenColor) { //this is a little <key,value> table permanently kept in memory SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE); //pair <key,value> to be stored represents our 'important' data SharedPreferences.Editor myPrefEditor = myPrefContainer.edit(); String key = "chosenBackgroundColor"; String value = txtSpyBox.getText().toString(); myPrefEditor.putString(key, value); myPrefEditor.commit(); }//saveStateData 3 - 40 Application's Life Cycle Example: The LifeCycle App - Code: MainActivity.java pp.7 private void updateMeUsingSavedStateData() { // (in case it exists) use saved data telling backg color SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE); String key = "chosenBackgroundColor"; String defaultValue = "white"; if (( myPrefContainer != null ) && myPrefContainer.contains(key)){ String color = myPrefContainer.getString(key, defaultValue); setBackgroundColor(color, myScreen); } } //updateMeUsingSavedStateData } //Activity 3 - 41 Example: The LifeCycle App - Code: MainActivity.java pp.8 3 - 42 Example: The LifeCycle App - Code: MainActivity.java pp.9 “ll I 5:53 3 - 43 Example: The LifeCycle App - Code: MainActivity.java pp.10 *4\ ! 6:28 C\Cookie LifeCycle User selects a green background and clicks Exit. When the app is paused the user's selection is saved and the app finally terminates. The app is re-executed Saved state information defining background color is reused by the new app's instance. Life cycle begins on the onCreate state 3 - 44 Example: The LifeCycle App - Code: MainActivity.java pp.11 The app is re-started and becomes visible again, showing all the state values previously set by the user (see the text boxes) User selects a green background and clicks the HOME key. When the app is paused the user's selection is saved, the app is still active but it is not visible. 3 - 45 Application's Life Cycle Questions? Appendix A: Using Bundles to Save/Restore State Values @Override public void onCreate(Bundle savedInstanceState) { • •• String someStrValue = savedInstanceState.getString("STR_KEY"); } @Override public void onPause() { Bundle myBundle = new Bundle(); myBundle.putString("STR_KEY", "blah blah blah"); } onSaveInstanceState( myBundle ); 3 - 46 Application's Life Cycle Questions? Appendix A: Using Bundles to Save/Restore State Values @Override public void onCreate(Bundle savedInstanceState) { • •• if (savedInstanceState != null ) { strVar = savedInstanceState.getString("STR_KEY"); } } @Override public void onSaveInstanceState(Bundle outState) { outState.putString("STR_KEY", "blah-blah-blah" ); super.onSaveInstanceState(outState); } 3 - 47 Appendix B: Detecting Device Rotation 1 of 2 The function below allows you to obtain the current ORIENTATION of the device as NORTH(0), WEST(1), SOUTH(2) and EAST(3). private int getOrientation(){ // the TOP of the device points to [0:North, 1:West, 2:South, 3:East] Display display = ((WindowManager) getApplication() .getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); display.getRotation(); return display.getRotation(); } ^ North: 0 top West: 1 — > top South: 2 ^ 3 - 48 Appendix B: Detecting Device Rotation 2 of 2 Use the onCreate method to initialize a control variable with the original device's orientation. During onPause compare the current orientation with its original value; if they are not the same then the device was rotated. int originalOrientation; //used to detect orientation change @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); originalOrientation getOrientation(); = } @Override protected void onPause() { super.onPause(); if( getOrientation() != originalOrientation ){ // Orientation changed - phone was rotated // put a flag in outBundle, call onSaveInstanceState(...) }else { // no orientation change detected in the session } } 3 - 49 Android Applications Lifecycle Wrap-Up Android Programming Components • Activity – http://developer.android.com/guide/topics/fundamentals/activities.html • Service – http://developer.android.com/guide/topics/fundamentals/services.html • Content Providers • Broadcast Receivers • Android in a nutshell: – http://developer.android.com/guide/topics/fundamentals.html Activities (1) • The basis of android applications • A single Activity defines a single viewable screen – the actions, not the layout • Can have multiple per application • Each is a separate entity • They have a structured life cycle – Different events in their life happen either via the user touching buttons or programmatically Activities (2) Services (1) • Run in the background – Can continue even if Activity that started it dies – Should be used if something needs to be done while the user is not interacting with application • Otherwise, a thread is probably more applicable – Should create a new thread in the service to do work in, since the service runs in the main thread • Can be bound to an application – In which case will terminate when all applications bound to it unbind – Allows multiple applications to communicate with it via a common interface • Needs to be declared in manifest file • Like Activities, has a structured life cycle Services (2) Intents, Intent Filters, and Invoking Activities: Part I: Using Class Name Originals of Slides and Source Code for Examples: http://www.coreservlets.com/android-tutorial/ Idea • Android philosophy – Activities are small, single-screen units • Consequence – You need easy way to switch from one Activity to another • Approach: use Intent – An abstract description of an operation to be performed – Intent can refer to class name of Activity or a URI – If a URI, then Activity registers as handler for the scheme, host name, or MIME type of the URI – In all cases, new Activity must have entry in AndroidManifest.xml in order to be invoked 58 Summary of Options • Invoke Activity by class name (Part I) – Exactly one Activity can match – New Activity must be in same project as original – Can send data via an “extras” Bundle • Invoke Activity by URI (Part II) – More than one Activity could match – New Activity need not be in the same project as original – Can send data via URI parameters or “extras” Bundle • Switch Activities via tabs (Not covered) – Can use class name or URI to specify Activity – New Activity must be in same project as original – Can send data via URI parameters or “extras” Bundle 59 Example Target Activity: Loan Payment Calculator Example Target Activity: Loan Calculator • Inputs – Loan amount – Interest rate (as a percent) – Loan period in months • Outputs – Monthly payment – Total payments over life of loan • Both are in same (e.g., dollars) as the loan amount units • Defaults – Unless values are passed in from other Activity, uses default values for all inputs 61 Summary of Layout Entries in first column are right-aligned. Entries in second column are left-aligned. They are also given ids so that the Java code can insert the text. This is a View with android:column_span="2" and a fixed height. TableLayout 62 XML: Layout File: First Row (res/layout/loan_payment.xml) <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://..." android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="1"> <TableRow android:layout_marginTop="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/default_foreground" android:textSize="@dimen/font_size" android:text="@string/loan_amount_prompt" android:gravity="right"/> <TextView android:id="@+id/loan_amount" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/default_foreground" android:textSize="@dimen/font_size" android:gravity="left"/> </TableRow> Second and third rows are very similar. Bottom two rows are almost the same except for a different textColor for the second column. 63 XML: Layout File: Divider (res/layout/loan_payment.xml) <TableRow> <TextView android:layout_span="2" android:layout_width="match_parent" android:layout_height="5dp" android:background="@color/divider_background"/> </TableRow> 64 XML: Strings File (res/values/strings.xml) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Intent Filters and Activity Switching</string> <string name="loan_calculator_app_name"> Loan Calculator: Monthly Payments </string> <string name="tabs_app_name">Tabbed Windows</string> <string name="loan_amount_prompt">Loan amount:  </string> <string name="interest_rate_prompt">Interest rate:  </string> <string name="loan_period_prompt">Months:  </string> <string name="monthly_payment_prompt">Monthly payment:  </string> <string name="total_payments_prompt">Total payments:  </string> </resources> The same prompts will also be used in a later input form. Note that   represents a non-breaking space. Regular spaces are not preserved at the beginning and end of strings in Android resource files. Note also that is not legal here, since that is a character entity specific to HTML, not general in XML. 65 XML: Colors File (res/values/colors.xml) <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Used inside loan_payments.xml. --> <color name="default_foreground">#d3d3d3</color> <color name="divider_background">#ffffff</color> <color name="result_foreground">#ff0000</color> </resources> 66 XML: Dimensions File (res/values/dimensions.xml) <?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="font_size">20dp</dimen> </resources> If you haven't seen a dimensions file before, note that the file name is arbitrary, as with all of the resource files in res/values. However, dimensions.xml is a common convention. Since they come with units (dp, sp, px, in, mm), you cannot store dimensions as regular strings. Dimensions are supplied via "@dimen/some_name" to attributes that expect font sizes, margin sizes, widths, heights, etc. In this case, "@dimen/font_size" was supplied for the android:textSize attribute of each of the TextViews. 67 Java (LoanCalculatorActivity.java) public class LoanCalculatorActivity extends Activity { private double mLoanAmount=100000, mAnnualInterestRateInPercent=5.0; private long mLoanPeriodInMonths=360; // 30 years private String mCurrencySymbol = "$"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.loan_payments); setInputsFromExtras(); setInputsFromUri(); calculateAndSetOutputValues(); } We will explain setInputsFromExtras in an upcoming subsection. We will explain setInputsfromUri in Part II of the Intents series. For now, these methods make no changes to the instance variables, and the default values of the instance variables (shown at the top) will be used by calculateAndSetOutputValues. 68 Java, Continued (LoanCalculatorActivity.java) private void calculateAndSetOutputValues() { PaymentInfo paymentInfo = new PaymentInfo(mLoanAmount, mAnnualInterestRateInPercent, mLoanPeriodInMonths, mCurrencySymbol); TextView loanAmountDisplay = (TextView)findViewById(R.id.loan_amount); loanAmountDisplay.setText(paymentInfo.getFormattedLoanAmount()); TextView interestRateDisplay = (TextView)findViewById(R.id.interest_rate); interestRateDisplay.setText (paymentInfo.getFormattedAnnualInterestRateInPercent()); TextView loanPeriodDisplay = (TextView)findViewById(R.id.loan_period); loanPeriodDisplay.setText(paymentInfo.getFormattedLoanPeriodInMonths()); TextView monthlyPaymentDisplay = (TextView)findViewById(R.id.monthly_payment); monthlyPaymentDisplay.setText(paymentInfo.getFormattedMonthlyPayment()); TextView totalPaymentsDisplay = (TextView)findViewById(R.id.total_payments); totalPaymentsDisplay.setText(paymentInfo.getFormattedTotalPayments()); } The math is done in the PaymentInfo class (next slides) at the top of the method, which in turn calls the LoanUtils class (following slides). The rest of the code just assigns the output values to the TextViews that are in the second column of the table from the layout file (res/layouts/loan_payments.xml). 69 Java (PaymentInfo.java) public class PaymentInfo { private final double mLoanAmount, mAnnualInterestRateInPercent, mMonthlyPayment, mTotalPayments; private final long mLoanPeriodInMonths; private final String mCurrencySymbol; public PaymentInfo(double loanAmount, double annualInterestRateInPercent, long loanPeriodInMonths, String currencySymbol) { mLoanAmount = loanAmount; mAnnualInterestRateInPercent = annualInterestRateInPercent; mLoanPeriodInMonths = loanPeriodInMonths; mCurrencySymbol = currencySymbol; mMonthlyPayment = LoanUtils.monthlyPayment(loanAmount, annualInterestRateInPercent, loanPeriodInMonths); mTotalPayments = mMonthlyPayment * mLoanPeriodInMonths; } ... } The remaining methods are just getter methods and variations of the getter methods that return the values as formatted strings (e.g., with commas and with exactly two values after the decimal point). 70 Java (LoanUtils.java) public class LoanUtils { public static double monthlyPayment(double loanAmount, double annualInterestRateInPercent, long loanPeriodInMonths) { if (annualInterestRateInPercent <= 0) { annualInterestRateInPercent = 0.0000001; } double monthlyInterestRate = annualInterestRateInPercent / 1200.0; double numerator = loanAmount * monthlyInterestRate; double denominator = 1 – Math.pow(1 + monthlyInterestRate, -1 * loanPeriodInMonths); return (numerator / denominator); } } Formula taken from http://www.financeformulas.net/Loan_Payment_Formula.html and http://en.wikipedia.org/wiki/Mortgage_calculator#Monthly_payment_formula 71 Example: Results For now, these values are fixed to the initial values set for the instance variables of the LoanCalculatorActivity. However, the upcoming sections will show how to pass values from another Activity to this one. Computed by LoanUtils. Formatted by PaymentInfo. 72 Invoking Activities by Class Name Summary • Idea – Specify class name of new Activity • New Activity must be in same project as original Activity • Syntax – Java (original Activity) Intent activityIntent = new Intent(this, NewActivity.class); startActivity(activityIntent); – XML (AndroidManifest.xml) <activity android:name=".NewActivity" android:label="@string/some_app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> 74 Example: Invoking Loan Calculator with Default Values Example: Overview • Initial Activity – Has Button that, when invokes the loan calculator activity pressed, • No data is sent to the loan calculator, so it will use default values for the loan amount, interest rate, etc. • Approach – Create Intent referring LoanCalculatorActivity.class to • Thus, the two Activities must be in same project – Call startActivity – Put entry for LoanCalculatorActivity in AndroidManifest.xml • So that the initial Activity has permission to invoke the loan calculator 76 XML: Layout File (res/layout/main.xml) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://..." android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:text="Calculate Loan Payments Data)" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:onClick="showLoanPayments1"/> ... </LinearLayout> Other buttons shown later 77 (No XML: Manifest File Template (AndroidManifest.xml) <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coreservlets.intentfilter1" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> This part is generated <activity android:name=".IntentFilter1Activity" automatically when you build a new Android project in Eclipse. android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> Means that this is ... the Action that runs </application> when you run the Other Activities will be </manifest> Means that this app project. declared here. See next slide for LoanCalculatorActivity. gets an icon on the screen of your Android device. 78 XML: Manifest File Action Declaration <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coreservlets.intentfilter1" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" Use the fully-qualified name if the new Activity is in a different package than the main one (i.e., the android:label="@string/app_name"> one listed at the top in the "manifest" start tag). ... <!-- Declaration for IntentFilter1Activity on previous slide --> <activity android:name=".LoanCalculatorActivity" android:label="@string/loan_calculator_app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> ... <!-- "data" entry shown later; not used in this example --> </intent-filter> </activity> Means that this Means that this Action displays data ... Action can be the to the user, but is not </application> default for certain launched as the initial types of data (shown Activity of the project. </manifest> later). The "data" tag of the land the "activity" tag for the tabbed windows Activity are both shown later. You virtually always set action.VIEW and category.DEFAULT for Activities that will be invoked by other Activities. 79 XML: Strings File (res/values/strings.xml) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name"> Intent Filters and Activity Switching </string> ... </resources> Other strings shown earlier, and apply to the LoanCalculatorActivity, not to the initial Activity with the buttons that launch the loan calculator. 80 Java (IntentFilter1Activity.java) public class IntentFilter1Activity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void showLoanPayments1(View clickedButton) { Intent activityIntent = new Intent(this, LoanCalculatorActivity.class); startActivity(activityIntent); } ... } Event handler methods for other buttons shown later 81 Example: Results 82 Sending Data via the “Extras” Bundle Summary • Idea – Attach a Bundle (like a Map – see next slides) to the Intent. The Bundle will contain data to be used by the new Activity. • Syntax – Java (original Activity) Intent activityIntent = new Intent(this, NewActivity.class); Bundle newActivityInfo = new Bundle(); newActivityInfo.putBlah(…); // putDouble, putString, etc. activityIntent.putExtras(newActivityInfo); startActivity(activityIntent); – Java (new Activity) Intent intent = getIntent(); Bundle info = intent.getExtras(); if (info != null) { /* Retrieve vals with info.getBlah(...) */ } 84 The Bundle Class: Details • Putting data in a Bundle – putBoolean, putBooleanArray, putDouble, putDoubleArray, putString, putStringArray, putStringArrayList etc. • These all take keys and values as arguments. The keys must be Strings. The values must be of the standard types (int, double, etc.) or array of them. – You can also make a custom class that implements Serializable or Parceleable, then store instance of that class with putSerializable or putParceleable • Methods return void, so you cannot chain as with the putExtra method of Intent. • Retrieving data from a Bundle – getBoolean, getBooleanArray, getDouble, getString, getStringArray, getStringArrayList, etc. • These take keys (Strings) No typecast required on retrieval. getDoubleArray, as arguments. 85 Option 1: Attaching Entire Bundle to Intent • Idea – Make a Bundle, add it all at once to Intent. • Instantiate a Bundle, then use the Bundle’s putBlah method (one such method for each standard type). Then, attach Bundle to Intent with Intent’s putExtras method. • Syntax Bundle newActivityInfo = new Bundle(); newActivityInfo.putDouble("key1", someDouble); newActivityInfo.putString("key2", someString); … yourIntent.putExtras(newActivityInfo); – Note that it is putExtras, not putExtra 86 Option 2: Adding One Piece of Data at a Time to Intent • Idea – Add individual pieces of data to the Intent. No need to explicitly create and attach a Bundle. • You use the overloaded “putExtra” method. The first argument is the key (String), and the second argument is the value, which can be of any standard type. However, the code that retrieves the value later needs to know type. • Syntax yourIntent.putExtra("key1", someDouble); yourIntent.putExtra("key2", someString); … – Unlike putBlah for Bundle, these putExtra methods return the Intent, so you can do jQuery-like chaining: » yourIntent.putExtra(…).putExtra(…) … .putExtra(…); 87 Example: Invoking Loan Calculator with Custom Values Example: Overview • Initial Activity – Has Button that, when invokes the loan calculator activity pressed, • Creates randomized data sends it to the loan calculator, retrieves the values and uses for its calculations. and which them • Approach – Create Intent referring LoanCalculatorActivity.class – Create Bundle with 3 values to • Loan amount, interest rate, loan period – Attach Bundle to Intent with putExtras – Call startActivity – Put entry for LoanCalculatorActivity manifest in 89 XML: Layout File (res/layout/main.xml) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://..." android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> ... <Button android:text="Calculate Loan Payments (with Data)" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:onClick="showLoanPayments2"/> ... First button shown earlier </LinearLayout> 90 XML: Manifest File Action Declaration <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coreservlets.intentfilter1" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> ... <!-- Declaration for IntentFilter1Activity on previous slide --> <activity android:name=".LoanCalculatorActivity" android:label="@string/loan_calculator_app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> ... <!-- "data" entry shown later; not used in this example --> </intent-filter> </activity> ... </application> </manifest> No changes from previous example. 91 Java (IntentFilter1Activity.java) public class IntentFilter1Activity extends Activity { ... public void showLoanPayments2(View clickedButton) { Intent activityIntent = new Intent(this, LoanCalculatorActivity.class); activityIntent. putExtras(LoanBundler.makeRandomizedLoanInfoBundle()); startActivity(activityIntent); } ... } Code for onCreate and first button’s event handler shown earlier. 92 Java (LoanBundler.java) public class LoanBundler { public static Bundle makeLoanInfoBundle(double loanAmount, double annualInterestRateInPercent, long loanPeriodInMonths, String currencySymbol) { Bundle loanInfo = new Bundle(); loanInfo.putDouble("loanAmount", loanAmount); loanInfo.putDouble("annualInterestRateInPercent", annualInterestRateInPercent); loanInfo.putLong("loanPeriodInMonths", loanPeriodInMonths); loanInfo.putString("currencySymbol", currencySymbol); return(loanInfo); } 93 Java (LoanBundler.java, Continued) public static Bundle makeRandomizedLoanInfoBundle() { Random randomizer = new Random(); double loanAmount = 25000 * (1 + randomizer.nextInt(10)); double interestRate = 0.25 * (1 + randomizer.nextInt(60)); long loanPeriod = 12 * (1 + randomizer.nextInt(30)); return(LoanBundler.makeLoanInfoBundle(loanAmount, interestRate, loanPeriod)); } 94 Java (LoanCalculatorActivity.java) public class LoanCalculatorActivity extends Activity { private double mLoanAmount=100000, mAnnualInterestRateInPercent=5.0; private long mLoanPeriodInMonths=360; // 30 years private String mCurrencySymbol = "$"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.loan_payments); setInputsFromExtras(); setInputsFromUri(); calculateAndSetOutputValues(); } 95 Java (LoanCalculatorActivity, Continued) private void setInputsFromExtras() { Intent intent = getIntent(); Bundle loanInfo = intent.getExtras(); if (loanInfo != null) { double loanAmount = loanInfo.getDouble("loanAmount"); double annualInterestRateInPercent = loanInfo.getDouble("annualInterestRateInPercent"); long loanPeriodInMonths = loanInfo.getLong("loanPeriodInMonths"); String currencySymbol = loanInfo.getString("currencySymbol"); setInputs(loanAmount, annualInterestRateInPercent, loanPeriodInMonths, currencySymbol); } } 96 Java (LoanCalculatorActivity, Continued) private void setInputs(double loanAmount, double annualInterestRateInPercent, long loanPeriodInMonths, String currencySymbol) { if (loanAmount > 0) { mLoanAmount = loanAmount; } if (annualInterestRateInPercent > 0) { mAnnualInterestRateInPercent = annualInterestRateInPercent; } if (loanPeriodInMonths > 0) { mLoanPeriodInMonths = loanPeriodInMonths; } if (currencySymbol != null) { mCurrencySymbol = currencySymbol; } } 97 Example: Results 98 Wrap-Up More Reading • Tutorial: Intents and Intent Filters – http://developer.android.com/guide/topics/intents/ intents-filters.html • JavaDoc: Intent – http://developer.android.com/reference/android/content/ Intent.html • Chapters: Creating Intent Filters Launching Activities and Sub-Activities and – From The Busy Coder’s Guide to Android Development • http://commonsware.com/Android/ • Chapter: Intents and Services – From Android in Action by Ableson et al 100 Summary • Java (original Activity) Intent activityIntent = new Intent(this, NewActivity.class); Bundle newActivityInfo = new Bundle(); newActivityInfo.putBlah(…); // putDouble, putString, etc. activityIntent.putExtras(newActivityInfo); startActivity(activityIntent); • Java (new Activity) Intent intent = getIntent(); Bundle info = intent.getExtras(); if (info != null) { /* Retrieve vals with info.getBlah(...) */ } • XML (AndroidManifest.xml) <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> 101 Intents, Intent Filters, and Invoking Activities: Part II: Using URI Topics in This Section • Part I – Invoking Activities by class name – Defining dimensions in res/values – Sending data via the “extras” Bundle • Part II – Invoking Activities with a URI – Sending data via parameters in the URI • Part III – Invoking Activities with tabbed windows – Defining two-image icons in res/drawable 103 Overview Summary of Options • Invoke Activity by class name (Part I) – Exactly one Activity can match – New Activity must be in same project as original – Can send data via an “extras” Bundle • Invoke Activity by URI (Part II) – More than one Activity could match – New Activity need not be in the same project as original – Can send data via URI parameters or “extras” Bundle • Switch Activities via tabs (Part III) – Can use class name or URI to specify Activity – New Activity must be in same project as original – Can send data via URI parameters or “extras” Bundle 105 Invoking Activities with a URI Summary • Idea – Supply a URI that indirectly refers to new Activity. The new Activity registers as target for URIs of a certain form. • The originating Activity and the new Activity need not be in the same project • More than one Activity could match the URI. – If so, Android will ask you which one to use. • Syntax – Java (original Activity) Uri uri = Uri.parse("foo://bar.example.com/baz"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(activityIntent); – XML (AndroidManifest.xml) <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="foo" android:host="bar.example.com" /> </intent-filter> 107 Registering to Handle URIs • Matching the URI itself – Register for a scheme and a host • Example URI – loan://coreservlets.com/calc • intent-filter entry – <data android:scheme="loan" android:host="coreservlets.com" /> – Note that the “calc” part is arbitrary – just to make URL look better. • Matching the data type – Register for a MIME type • Example URIs – content:// (referring to that MIME type) – file:// (referring to that MIME type) – anything:// (the Intent can call setType to specify MIME type) • intent-filter entry – <data android:mimeType="some/type" /> – <data android:mimeType="something/*" /> 108 Predefined Action/URI Combinations Action URI Meaning Intent.ACTION_CALL tel:phone_number Opens phone application and calls phone_number. Intent.ACTION_DIAL tel:phone_number Opens phone application and dials (but doesn’t call) phone_number. Intent.ACTION_DIAL voicemail: Opens phone application and dials (but doesn’t call) the voice mail number. Intent.ACTION_VIEW geo:lat,long Opens the maps application centered on (lat, long). Intent.ACTION_VIEW geo:0,0?q=address Opens the maps application centered on the specified address. Intent.ACTION_VIEW http://url https://url Opens the browser application to the specified address. Intent. ACTION_WEB_SEARCH 109 plain_text Opens the browser application and uses Google search for given string. Table adapted from Section 4.1.5 of Android in Action by Ableson et al. Example: Invoking Loan Calculator (Data in Extras Bundle) Example: Overview • Initial Activity – Has Button that, invokes the loan calculator activity when pressed, • Initial Activity uses URI to indirectly invoke loan calculator • Initial Activity is in different project than loan calculator • Data is sent via extras Bundle as in previous example • Approach – Create Intent with Intent.ACTION_VIEW "loan://coreservlets.com/calc“ and URI • The “calc” at the end is arbitrary – just for aesthetics – Create and attach Bundle as in previous example – Call startActivity – Put data entry for LoanCalculatorActivity in manifest – <data android:scheme="loan" android:host="coreservlets.com" /> 111 of XML: Layout File (res/layout/main.xml – 2nd Proj.) <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="1"> <TableRow> <Button android:text="Calculate Loan Payments (Data in Extras)" android:layout_span="2" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="showLoanPayments1"/> </TableRow> ... </TableLayout> Entries for input form and second button shown later. 112 XML: Manifest File Action Declaration (Loan Proj.) <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coreservlets.intentfilter1" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> ... <!-- Declaration for IntentFilter1Activity shown earlier --> <activity android:name=".LoanCalculatorActivity" android:label="@string/loan_calculator_app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="loan" android:host="coreservlets.com" /> </intent-filter> </activity> ... </application> </manifest> 113 Java (IntentFilter2Activity.java) public class IntentFilter2Activity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void showLoanPayments1(View clickedButton) { Uri uri = Uri.parse("loan://coreservlets.com/calc"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtras (LoanBundler.makeRandomizedLoanInfoBundle()); startActivity(intent); } ... } Code for second button (that embeds data in the URI) shown later. 114 Java Code Shown Earlier • LoanBundler – Makes a Bundle that stores the loan amount, interest rate, and loan period • LoanCalculatorActivity – Calls getIntent().getExtras() and reads the data out of the resultant Bundle. Uses that for the initial values for the loan amount, interest rate, and loan period – Passes the values to PaymentInfo, which in turn uses LoanUtils to calculate monthly payment and total payments – Puts all five values (loan amount, interest rate, loan period, monthly payment, total payments) into TextViews 115 Example: Results 116 Sending Data via Parameters in the URI Summary • Idea – Embed query parameters in the URI. These parameters will represent data to be used by the new Activity. • Syntax – Java (original Activity) String address = "loan://coreservlets.com/calc?loanAmount=xxx&…"; Uri uri = Uri.parse(address); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(activityIntent); – Java (new Activity) Uri uri = getIntent().getData(); String loanAmountString = uri.getQueryParameter("loanAmount"); // Convert String to double ... 118 Sending Data: Extras vs. URI Parameters • Extras Bundle – Pros • Can send data of different types. • No parsing required in Activity that receives the data. – Cons • More complex for originating Activity – Requires parsing in originating Activity if values come from EditText • URI parameters – Pros • Simpler for originating Activity, especially if EditText used • More consistent with URI usage – Cons • Can send Strings only • Requires parsing in receiving Activity 119 Example: Invoking Loan Calculator (Data in URI Parameters) Example: Overview • Initial Activity – Has Button that, invokes the loan calculator activity when pressed, • Data is extracted from textfields (EditTexts) and embedded in the URI that is used to invoke loan calculator • Approach – Create Intent with Intent.ACTION_VIEW "loan://coreservlets.com/calc?data" and URI • Data is "loanAmount=…&annualInterestRateInPercent=…&…" – Call startActivity – Put data entry for LoanCalculatorActivity in manifest – <data android:scheme="loan" android:host="coreservlets.com" /> 121 of XML: Layout File (res/layout/main.xml – 2nd Proj.) <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="1"> ... <TableRow android:layout_marginTop="30dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/loan_amount_prompt" android:gravity="right"/> <EditText android:id="@+id/loan_amount" android:inputType="numberDecimal" android:layout_height="wrap_content"> <requestFocus></requestFocus> </EditText> </TableRow> ... Entry for first button shown earlier. Entries for other textfields </TableLayout> (EditTexts) similar to the one shown. Entry for button at the bottom just has android:onClick="showLoanPayments2". 122 XML: Strings File (res/values/strings.xml) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Intent Filters and Activity Switching</string> <string name="loan_calculator_app_name"> Loan Calculator: Monthly Payments </string> <string name="tabs_app_name">Tabbed Windows</string> <string name="loan_amount_prompt">Loan amount:  </string> <string name="interest_rate_prompt">Interest rate:  </string> <string name="loan_period_prompt">Months:  </string> <string name="monthly_payment_prompt">Monthly payment:  </string> <string name="total_payments_prompt">Total payments:  </string> </resources> The same prompts are also used in the output display. Note that   represents a non-breaking space. Regular spaces are not preserved at the beginning and end of strings in Android resource files. Note also that is not legal here, since that is a character entity specific to HTML, not general in XML. 123 XML: Manifest File Action Declaration (Loan Proj.) <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coreservlets.intentfilter1" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> ... <!-- Declaration for IntentFilter1Activity shown earlier --> <activity android:name=".LoanCalculatorActivity" android:label="@string/loan_calculator_app_name"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="loan" android:host="coreservlets.com" /> </intent-filter> </activity> ... </application> Unchanged from previous example. </manifest> 124 Java (IntentFilter2Activity.java) public class IntentFilter2Activity extends Activity { ... public void showLoanPayments2(View clickedButton) { String address = makeLoanAddressFromEditTextInputs(); Uri uri = Uri.parse(address); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } Code for onCreate and first button shown earlier. 125 Java (IntentFilter2Activity, Continued) private String makeLoanAddressFromEditTextInputs() { EditText loanAmountInput = (EditText)findViewById(R.id.loan_amount); Editable loanAmount = loanAmountInput.getText(); String loanAmountParam = String.format("loanAmount=%s", loanAmount); EditText interestRateInput = (EditText)findViewById(R.id.interest_rate); Editable interestRate = interestRateInput.getText(); String interestRateParam = String.format("annualInterestRateInPercent=%s", interestRate); EditText loanPeriodInput = (EditText)findViewById(R.id.loan_period); Editable loanPeriod = loanPeriodInput.getText(); String loanPeriodParam = String.format("loanPeriodInMonths=%s", loanPeriod); String baseAddress = "loan://coreservlets.com/calc"; String address = String.format("%s?%s&%s&%s", baseAddress, loanAmountParam, interestRateParam, loanPeriodParam); return(address); } 126 Java (LoanCalculatorActivity.java) public class LoanCalculatorActivity extends Activity { private double mLoanAmount=100000, mAnnualInterestRateInPercent=5.0; private long mLoanPeriodInMonths=360; // 30 years private String mCurrencySymbol = "$"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.loan_payments); setInputsFromExtras(); setInputsFromUri(); calculateAndSetOutputValues(); } 127 Java (LoanCalculatorActivity, Continued) private void setInputsFromUri() { Uri uri = getIntent().getData(); if (uri != null) { double loanAmount = getDoubleParam(uri, "loanAmount"); double annualInterestRateInPercent = getDoubleParam(uri, "annualInterestRateInPercent"); long loanPeriodInMonths = getLongParam(uri, "loanPeriodInMonths"); String currencySymbol = uri.getQueryParameter("currencySymbol"); setInputs(loanAmount, annualInterestRateInPercent, loanPeriodInMonths, currencySymbol); } } getQueryParameter is the builtin method of Uri. getDoubleParam and getLongParam (next slides) are methods of LoanCalculatorActivity that call getQueryParameter and then parse the resultant String. 128 Java (LoanCalculatorActivity, Continued) private void setInputsFromUri() { Uri uri = getIntent().getData(); if (uri != null) { double loanAmount = getDoubleParam(uri, "loanAmount"); double annualInterestRateInPercent = getDoubleParam(uri, "annualInterestRateInPercent"); long loanPeriodInMonths = getLongParam(uri, "loanPeriodInMonths"); String currencySymbol = uri.getQueryParameter("currencySymbol"); setInputs(loanAmount, annualInterestRateInPercent, loanPeriodInMonths, currencySymbol); } } 129 Java (LoanCalculatorActivity, Continued) private double getDoubleParam(Uri uri, String queryParamName) { String rawValue = uri.getQueryParameter(queryParamName); double value = 0.0; try { value = Double.parseDouble(rawValue); } catch(Exception e) { } // NumberFormatEx or NullPointerEx return(value); } private long getLongParam(Uri uri, String queryParamName) { String rawValue = uri.getQueryParameter(queryParamName); long value = 0; try { value = Long.parseLong(rawValue); } catch(Exception e) { } // NFE or NPE return(value); } 130 Example: Results 131 Wrap-Up Summary • Java (original Activity) String address = "loan://coreservlets.com/calc?loanAmount=xxx&…"; Uri uri = Uri.parse(address); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(activityIntent); • Java (new Activity – can be different project) Uri uri = getIntent().getData(); String loanAmountString = uri.getQueryParameter("loanAmount"); // Convert String to double, handle bad data ... • XML (AndroidManifest.xml) <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="loan" android:host="coreservlets.com" /> </intent-filter> 133 Android Intents Appendix A. Built-In Intent Actions A complete list of built-in, broadcast, service actions, categories, and features for a particular SDK can be found in the folders: .../android-sdk/platforms/platformYYY/data/ android.app.action. ACTION_PASSWORD_CHANGED ACTION_PASSWORD_EXPIRING ACTION_PASSWORD_FAILED ACTION_PASSWORD_SUCCEEDED ADD_DEVICE_ADMIN DEVICE_ADMIN_DISABLE_REQUES TED DEVICE_ADMIN_DISABLED DEVICE_ADMIN_ENABLED SET_NEW_PASSWORD START_ENCRYPTION android.bluetooth.a2dp.profile.action. CONNECTION_STATE_CHANGED PLAYING_STATE_CHANGED android.bluetooth.adapter.action. CONNECTION_STATE_CHANGED DISCOVERY_FINISHED DISCOVERY STARTED LOCAL_NAME_CHANGED REQUEST_DISCOVERABLE REQUEST_ENABLE SCAN_MODE_CHANGED STATE_CHANGED android.bluetooth.device.action. ACL_CONNECTED ACL_DISCONNECT_REQUESTED ACL_DISCONNECTED BOND_STATE_CHANGED CLASS_CHANGED FOUND NAME_CHANGED UUID android.bluetooth.devicepicker.action. DEVICE_SELECTED LAUNCH 82 Android Intents Appendix A. Built-In Intent Actions cont. 1 android.bluetooth.headset. action.VENDOR_SPECIFIC_HEADSET_EVENT profile.action.AUDIO_STATE_CHANGED profile.action.CONNECTION_STATE_CHANGED android.hardware.action. NEW_PICTURE NEW_VIDEO input.action.QUERY_KEYBOARD_LAYOUTS android.intent.action. ACTION_POWER_CONNECTED ACTION_POWER_DISCONNECTED ACTION_SHUTDOWN AIRPLANE_MODE ALL_APPS ANSWER APP_ERROR ASSIST ATTACH_DATA BATTERY_CHANGED BATTERY_LOW BATTERY_OKAY BOOT_COMPLETED BUG_REPORT CALL CALL_BUTTON CAMERA_BUTTON CHOOSER CONFIGURATION_CHANGED CREATE_LIVE_FOLDER CREATE_SHORTCUT DATE_CHANGED DELETE DEVICE_STORAGE_LOW DEVICE_STORAGE_OK DIAL DOCK_EVENT DREAMING_STARTED DREAMING_STOPPED EDIT 83 Android Intents Appendix A. Built-In Intent Actions cont. 2 android.intent.action. EVENT_REMINDER EXTERNAL_APPLICATIONS_AVAILAB LE EXTERNAL_APPLICATIONS_UNAVAIL ABLE FETCH_VOICEMAIL GET_CONTENT GTALK_CONNECTED GTALK_DISCONNECTED HEADSET_PLUG INPUT_METHOD_CHANGED INSERT INSERT_OR_EDIT INSTALL_PACKAGE LOCALE_CHANGED MAIN MANAGE_NETWORK_USAGE MANAGE_PACKAGE_STORAGE MEDIA_BAD_REMOVAL MEDIA_BUTTON MEDIA CHECKING MEDIA_EJECT MEDIA_MOUNTED MEDIA_NOFS MEDIA_REMOVED MEDIA_SCANNER_FINISH ED MEDIA_SCANNER_SCAN_ FILE MEDIA_SCANNER_START ED MEDIA_SEARCH MEDIA_SHARED MEDIA_UNMOUNTABLE MEDIA_UNMOUNTED MUSIC_PLAYER MY_PACKAGE_REPLACED NEW_OUTGOING_CALL NEW_VOICEMAIL PACKAGE_ADDED PACKAGE_CHANGED PACKAGE_DATA_CLEARE D PACKAGE_FIRST_LAUNCH PACKAGE FULLY 84 Android Intents Appendix A. Built-In Intent Actions cont. 3 android.intent.action. PACKAGE_INSTALL PACKAGE_NEEDS_VERIFICA TION PACKAGE_REMOVED PACKAGE_REPLACED PACKAGE_RESTARTED PACKAGE_VERIFIED PASTE PHONE_STATE PICK PICK_ACTIVITY POWER_USAGE_SUMMARY PROVIDER_CHANGED PROXY_CHANGE REBOOT RESPOND_VIA_MESSAGE RINGTONE_PICKER RUN SCREEN_OFF SCREEN_ON SEARCH SEARCH_LONG_PRE SS SEND SEND_MULTIPLE SENDTO SET_ALARM SET_WALLPAPER SYNC SYSTEM_TUTORIAL TIME_SET TIME_TICK TIMEZONE_CHANGE D UID_REMOVED UNINSTALL_PACKAG E USER_PRESENT VIEW VOICE_COMMAND WALLPAPER_CHANG ED WEB_SEARCH 85 Android Intents Appendix A. Built-In Intent Actions cont. 4 android.media. action.CLOSE_AUDIO_EFFECT_CONTROL_SE SSION action.DISPLAY_AUDIO_EFFECT_CONTROL_P ANEL action.OPEN_AUDIO_EFFECT_CONTROL_SE SSION ACTION_SCO_AUDIO_STATE_UPDATED AUDIO_BECOMING_NOISY RINGER_MODE_CHANGED android.net. SCO_AUDIO_STATE_CHANGED conn.BACKGROUND_DATA_SETTING_CHAN VIBRATE SETTING CHANGED GED conn.CONNECTIVITY_CHANGE nsd.STATE_CHANGED wifi.action.REQUEST_SCAN_ALWAYS_AVAIL ABLE wifi.NETWORK_IDS_CHANGED wifi.p2p.CONNECTION_STATE_CHANGE wifi.p2p.DISCOVERY_STATE_CHANGE wifi.p2p.PEERS_CHANGED wifi.p2p.STATE_CHANGED wifi.p2p.THIS_DEVICE_CHANGED wifi.PICK_WIFI_NETWORK wifi.RSSI_CHANGED wifi.SCAN_RESULTS wifi.STATE_CHANGE wifi.supplicant.CONNECTION_CHAN GE wifi.supplicant.STATE_CHANGE wifi.WIFI STATE CHANGED android.nfc.action. ADAPTER_STATE_CHAN GED NDEF_DISCOVERED TAG_DISCOVERED TECH DISCOVERED 86 Android Intents Appendix A. Built-In Intent Actions cont. 5 android.settings. ACCESSIBILITY_SETTINGS ADD_ACCOUNT_SETTINGS AIRPLANE_MODE_SETTINGS APN_SETTINGS APPLICATION_DETAILS_SETTINGS APPLICATION_DEVELOPMENT_SETT INGS APPLICATION_SETTINGS BLUETOOTH_SETTINGS DATA_ROAMING_SETTINGS DATE_SETTINGS DEVICE_INFO_SETTINGS DISPLAY_SETTINGS DREAM_SETTINGS INPUT_METHOD_SETTINGS INPUT_METHOD_SUBTYPE_SETTIN GS INTERNAL_STORAGE_SETTINGS LOCALE_SETTINGS LOCATION_SOURCE_SETTINGS MANAGE_ALL_APPLICATIONS_SETTI NGS MANAGE APPLICATIONS SETTINGS MEMORY_CARD_SETTINGS NETWORK_OPERATOR_SETTINGS NFC_SETTINGS NFCSHARING_SETTINGS PRIVACY_SETTINGS QUICK_LAUNCH_SETTINGS SECURITY_SETTINGS SETTINGS SOUND_SETTINGS SYNC_SETTINGS USER_DICTIONARY_SETTINGS WIFI_IP_SETTINGS WIFI_SETTINGS WIRELESS_SETTINGS android.speech.tts. engine.CHECK_TTS_DATA engine.GET_SAMPLE_TEXT engine.INSTALL_TTS_DATA engine.TTS_DATA_INSTALLED TTS_QUEUE_PROCESSING_COMPL ETED 87 Ευχαριστώ για την προσοχή σας!