• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.stk;
18 
19 import android.app.ListActivity;
20 import android.app.Activity;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.view.ContextMenu;
27 import android.view.ContextMenu.ContextMenuInfo;
28 import android.view.KeyEvent;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.view.Window;
32 import android.widget.AdapterView;
33 import android.widget.ImageView;
34 import android.widget.ListView;
35 import android.widget.ProgressBar;
36 import android.widget.TextView;
37 
38 import com.android.internal.telephony.cat.Item;
39 import com.android.internal.telephony.cat.Menu;
40 import com.android.internal.telephony.cat.CatLog;
41 import android.telephony.TelephonyManager;
42 
43 /**
44  * ListActivity used for displaying STK menus. These can be SET UP MENU and
45  * SELECT ITEM menus. This activity is started multiple times with different
46  * menu content.
47  *
48  */
49 public class StkMenuActivity extends ListActivity implements View.OnCreateContextMenuListener {
50     private Context mContext;
51     private Menu mStkMenu = null;
52     private int mState = STATE_MAIN;
53     private boolean mAcceptUsersInput = true;
54     private int mSlotId = -1;
55     private boolean mIsResponseSent = false;
56     Activity mInstance = null;
57 
58     private TextView mTitleTextView = null;
59     private ImageView mTitleIconView = null;
60     private ProgressBar mProgressView = null;
61     private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
62     private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
63 
64     private StkAppService appService = StkAppService.getInstance();
65 
66     // Internal state values
67     static final int STATE_INIT = 0;
68     static final int STATE_MAIN = 1;
69     static final int STATE_SECONDARY = 2;
70 
71     // Finish result
72     static final int FINISH_CAUSE_NORMAL = 1;
73     static final int FINISH_CAUSE_NULL_SERVICE = 2;
74     static final int FINISH_CAUSE_NULL_MENU = 3;
75 
76     // message id for time out
77     private static final int MSG_ID_TIMEOUT = 1;
78     private static final int CONTEXT_MENU_HELP = 0;
79 
80     Handler mTimeoutHandler = new Handler() {
81         @Override
82         public void handleMessage(Message msg) {
83             switch(msg.what) {
84             case MSG_ID_TIMEOUT:
85                 CatLog.d(LOG_TAG, "MSG_ID_TIMEOUT mState: " + mState);
86                 mAcceptUsersInput = false;
87                 if (mState == STATE_SECONDARY) {
88                     appService.getStkContext(mSlotId).setPendingActivityInstance(mInstance);
89                 }
90                 sendResponse(StkAppService.RES_ID_TIMEOUT);
91                 //finish();//We wait the following commands to trigger onStop of this activity.
92                 break;
93             }
94         }
95     };
96 
97     @Override
onCreate(Bundle icicle)98     public void onCreate(Bundle icicle) {
99         super.onCreate(icicle);
100 
101         CatLog.d(LOG_TAG, "onCreate");
102         // Remove the default title, customized one is used.
103         requestWindowFeature(Window.FEATURE_NO_TITLE);
104         // Set the layout for this activity.
105         setContentView(R.layout.stk_menu_list);
106         mInstance = this;
107         mTitleTextView = (TextView) findViewById(R.id.title_text);
108         mTitleIconView = (ImageView) findViewById(R.id.title_icon);
109         mProgressView = (ProgressBar) findViewById(R.id.progress_bar);
110         mContext = getBaseContext();
111         mAcceptUsersInput = true;
112         getListView().setOnCreateContextMenuListener(this);
113         initFromIntent(getIntent());
114     }
115 
116     @Override
onListItemClick(ListView l, View v, int position, long id)117     protected void onListItemClick(ListView l, View v, int position, long id) {
118         super.onListItemClick(l, v, position, id);
119 
120         if (!mAcceptUsersInput) {
121             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
122             return;
123         }
124 
125         Item item = getSelectedItem(position);
126         if (item == null) {
127             CatLog.d(LOG_TAG, "Item is null");
128             return;
129         }
130 
131         CatLog.d(LOG_TAG, "onListItemClick Id: " + item.id + ", mState: " + mState);
132         // ONLY set SECONDARY menu. It will be finished when the following command is comming.
133         if (mState == STATE_SECONDARY) {
134             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
135         }
136         cancelTimeOut();
137         sendResponse(StkAppService.RES_ID_MENU_SELECTION, item.id, false);
138         mAcceptUsersInput = false;
139         mProgressView.setVisibility(View.VISIBLE);
140         mProgressView.setIndeterminate(true);
141     }
142 
143     @Override
onKeyDown(int keyCode, KeyEvent event)144     public boolean onKeyDown(int keyCode, KeyEvent event) {
145         CatLog.d(LOG_TAG, "mAcceptUsersInput: " + mAcceptUsersInput);
146         if (!mAcceptUsersInput) {
147             return true;
148         }
149 
150         switch (keyCode) {
151         case KeyEvent.KEYCODE_BACK:
152             CatLog.d(LOG_TAG, "KEYCODE_BACK - mState[" + mState + "]");
153             switch (mState) {
154             case STATE_SECONDARY:
155                 CatLog.d(LOG_TAG, "STATE_SECONDARY");
156                 cancelTimeOut();
157                 mAcceptUsersInput = false;
158                 appService.getStkContext(mSlotId).setPendingActivityInstance(this);
159                 sendResponse(StkAppService.RES_ID_BACKWARD);
160                 return true;
161             case STATE_MAIN:
162                 CatLog.d(LOG_TAG, "STATE_MAIN");
163                 appService.getStkContext(mSlotId).setMainActivityInstance(null);
164                 cancelTimeOut();
165                 finish();
166                 return true;
167             }
168             break;
169         }
170         return super.onKeyDown(keyCode, event);
171     }
172 
173     @Override
onRestart()174     public void onRestart() {
175         super.onRestart();
176         CatLog.d(LOG_TAG, "onRestart, slot id: " + mSlotId);
177     }
178 
179     @Override
onResume()180     public void onResume() {
181         super.onResume();
182 
183         CatLog.d(LOG_TAG, "onResume, slot id: " + mSlotId + "," + mState);
184         appService.indicateMenuVisibility(true, mSlotId);
185         if (mState == STATE_MAIN) {
186             mStkMenu = appService.getMainMenu(mSlotId);
187         } else {
188             mStkMenu = appService.getMenu(mSlotId);
189         }
190         if (mStkMenu == null) {
191             CatLog.d(LOG_TAG, "menu is null");
192             cancelTimeOut();
193             finish();
194             return;
195         }
196         //Set main menu instance here for clean up stack by other SIMs
197         //when receiving OP_LAUNCH_APP.
198         if (mState == STATE_MAIN) {
199             CatLog.d(LOG_TAG, "set main menu instance.");
200             appService.getStkContext(mSlotId).setMainActivityInstance(this);
201         }
202         displayMenu();
203         startTimeOut();
204         // whenever this activity is resumed after a sub activity was invoked
205         // (Browser, In call screen) switch back to main state and enable
206         // user's input;
207         if (!mAcceptUsersInput) {
208             //Remove set mState to STATE_MAIN. This is for single instance flow.
209             mAcceptUsersInput = true;
210         }
211         invalidateOptionsMenu();
212 
213         // make sure the progress bar is not shown.
214         mProgressView.setIndeterminate(false);
215         mProgressView.setVisibility(View.GONE);
216     }
217 
218     @Override
onPause()219     public void onPause() {
220         super.onPause();
221         CatLog.d(LOG_TAG, "onPause, slot id: " + mSlotId + "," + mState);
222         //If activity is finished in onResume and it reaults from null appService.
223         if (appService != null) {
224             appService.indicateMenuVisibility(false, mSlotId);
225         } else {
226             CatLog.d(LOG_TAG, "onPause: null appService.");
227         }
228 
229         /*
230          * do not cancel the timer here cancelTimeOut(). If any higher/lower
231          * priority events such as incoming call, new sms, screen off intent,
232          * notification alerts, user actions such as 'User moving to another activtiy'
233          * etc.. occur during SELECT ITEM ongoing session,
234          * this activity would receive 'onPause()' event resulting in
235          * cancellation of the timer. As a result no terminal response is
236          * sent to the card.
237          */
238 
239     }
240 
241     @Override
onStop()242     public void onStop() {
243         super.onStop();
244         CatLog.d(LOG_TAG, "onStop, slot id: " + mSlotId + "," + mIsResponseSent + "," + mState);
245         //The menu should stay in background, if
246         //1. the dialog is pop up in the screen, but the user does not response to the dialog.
247         //2. the menu activity enters Stop state (e.g pressing HOME key) but mIsResponseSent is false.
248         if (mIsResponseSent) {
249             // ONLY finish SECONDARY menu. MAIN menu should always stay in the root of stack.
250             if (mState == STATE_SECONDARY) {
251                 if (!appService.isStkDialogActivated(mContext)) {
252                     CatLog.d(LOG_TAG, "STATE_SECONDARY finish.");
253                     cancelTimeOut();//To avoid the timer time out and send TR again.
254                     finish();
255                 } else {
256                      if (appService != null) {
257                          appService.getStkContext(mSlotId).setPendingActivityInstance(this);
258                      }
259                 }
260             }
261         } else {
262             if (appService != null) {
263                 appService.getStkContext(mSlotId).setPendingActivityInstance(this);
264             } else {
265                 CatLog.d(LOG_TAG, "onStop: null appService.");
266             }
267         }
268     }
269 
270     @Override
onDestroy()271     public void onDestroy() {
272         getListView().setOnCreateContextMenuListener(null);
273         super.onDestroy();
274         CatLog.d(LOG_TAG, "onDestroy" + "," + mState);
275         //isMenuPending: if input act is finish by stkappservice when OP_LAUNCH_APP again,
276         //we can not send TR here, since the input cmd is waiting user to process.
277         if (!mIsResponseSent && !appService.isMenuPending(mSlotId)) {
278             CatLog.d(LOG_TAG, "handleDestroy - Send End Session");
279             sendResponse(StkAppService.RES_ID_END_SESSION);
280         }
281         if (mState == STATE_MAIN) {
282             if (appService != null) {
283                 appService.getStkContext(mSlotId).setMainActivityInstance(null);
284             } else {
285                 CatLog.d(LOG_TAG, "onDestroy: null appService.");
286             }
287         }
288     }
289 
290     @Override
onCreateOptionsMenu(android.view.Menu menu)291     public boolean onCreateOptionsMenu(android.view.Menu menu) {
292         super.onCreateOptionsMenu(menu);
293         menu.add(0, StkApp.MENU_ID_END_SESSION, 1, R.string.menu_end_session);
294         menu.add(0, StkApp.MENU_ID_HELP, 2, R.string.help);
295         return true;
296     }
297 
298     @Override
onPrepareOptionsMenu(android.view.Menu menu)299     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
300         super.onPrepareOptionsMenu(menu);
301         boolean helpVisible = false;
302         boolean mainVisible = false;
303 
304         if (mState == STATE_SECONDARY) {
305             mainVisible = true;
306         }
307         if (mStkMenu != null) {
308             helpVisible = mStkMenu.helpAvailable;
309         }
310 
311         menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(mainVisible);
312         menu.findItem(StkApp.MENU_ID_HELP).setVisible(helpVisible);
313 
314         return true;
315     }
316 
317     @Override
onOptionsItemSelected(MenuItem item)318     public boolean onOptionsItemSelected(MenuItem item) {
319         if (!mAcceptUsersInput) {
320             return true;
321         }
322         switch (item.getItemId()) {
323         case StkApp.MENU_ID_END_SESSION:
324             cancelTimeOut();
325             mAcceptUsersInput = false;
326             // send session end response.
327             sendResponse(StkAppService.RES_ID_END_SESSION);
328             cancelTimeOut();
329             finish();
330             return true;
331         case StkApp.MENU_ID_HELP:
332             cancelTimeOut();
333             mAcceptUsersInput = false;
334             int position = getSelectedItemPosition();
335             Item stkItem = getSelectedItem(position);
336             if (stkItem == null) {
337                 break;
338             }
339             // send help needed response.
340             sendResponse(StkAppService.RES_ID_MENU_SELECTION, stkItem.id, true);
341             return true;
342         }
343         return super.onOptionsItemSelected(item);
344     }
345 
346     @Override
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)347     public void onCreateContextMenu(ContextMenu menu, View v,
348             ContextMenuInfo menuInfo) {
349         CatLog.d(this, "onCreateContextMenu");
350         boolean helpVisible = false;
351         if (mStkMenu != null) {
352             helpVisible = mStkMenu.helpAvailable;
353         }
354         if (helpVisible) {
355             CatLog.d(this, "add menu");
356             menu.add(0, CONTEXT_MENU_HELP, 0, R.string.help);
357         }
358     }
359 
360     @Override
onContextItemSelected(MenuItem item)361     public boolean onContextItemSelected(MenuItem item) {
362         AdapterView.AdapterContextMenuInfo info;
363         try {
364             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
365         } catch (ClassCastException e) {
366             return false;
367         }
368         switch (item.getItemId()) {
369             case CONTEXT_MENU_HELP:
370                 cancelTimeOut();
371                 mAcceptUsersInput = false;
372                 int position = info.position;
373                 CatLog.d(this, "Position:" + position);
374                 Item stkItem = getSelectedItem(position);
375                 if (stkItem != null) {
376                     CatLog.d(this, "item id:" + stkItem.id);
377                     sendResponse(StkAppService.RES_ID_MENU_SELECTION, stkItem.id, true);
378                 }
379                 return true;
380 
381             default:
382                 return super.onContextItemSelected(item);
383         }
384     }
385 
386     @Override
onSaveInstanceState(Bundle outState)387     protected void onSaveInstanceState(Bundle outState) {
388         CatLog.d(LOG_TAG, "onSaveInstanceState: " + mSlotId);
389         outState.putInt("STATE", mState);
390         outState.putParcelable("MENU", mStkMenu);
391         outState.putBoolean("ACCEPT_USERS_INPUT", mAcceptUsersInput);
392     }
393 
394     @Override
onRestoreInstanceState(Bundle savedInstanceState)395     protected void onRestoreInstanceState(Bundle savedInstanceState) {
396         CatLog.d(LOG_TAG, "onRestoreInstanceState: " + mSlotId);
397         mState = savedInstanceState.getInt("STATE");
398         mStkMenu = savedInstanceState.getParcelable("MENU");
399         mAcceptUsersInput = savedInstanceState.getBoolean("ACCEPT_USERS_INPUT");
400     }
401 
cancelTimeOut()402     private void cancelTimeOut() {
403         CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId);
404         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
405     }
406 
startTimeOut()407     private void startTimeOut() {
408         if (mState == STATE_SECONDARY) {
409             // Reset timeout.
410             cancelTimeOut();
411             CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId);
412             mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
413                     .obtainMessage(MSG_ID_TIMEOUT), StkApp.UI_TIMEOUT);
414         }
415     }
416 
417     // Bind list adapter to the items list.
displayMenu()418     private void displayMenu() {
419 
420         if (mStkMenu != null) {
421             String title = mStkMenu.title == null ? getString(R.string.app_name) : mStkMenu.title;
422             // Display title & title icon
423             if (mStkMenu.titleIcon != null) {
424                 mTitleIconView.setImageBitmap(mStkMenu.titleIcon);
425                 mTitleIconView.setVisibility(View.VISIBLE);
426                 mTitleTextView.setVisibility(View.INVISIBLE);
427                 if (!mStkMenu.titleIconSelfExplanatory) {
428                     mTitleTextView.setText(title);
429                     mTitleTextView.setVisibility(View.VISIBLE);
430                 }
431             } else {
432                 mTitleIconView.setVisibility(View.GONE);
433                 mTitleTextView.setVisibility(View.VISIBLE);
434                 mTitleTextView.setText(title);
435             }
436             // create an array adapter for the menu list
437             StkMenuAdapter adapter = new StkMenuAdapter(this,
438                     mStkMenu.items, mStkMenu.itemsIconSelfExplanatory);
439             // Bind menu list to the new adapter.
440             setListAdapter(adapter);
441             // Set default item
442             setSelection(mStkMenu.defaultItem);
443         }
444     }
445 
initFromIntent(Intent intent)446     private void initFromIntent(Intent intent) {
447 
448         if (intent != null) {
449             mState = intent.getIntExtra("STATE", STATE_MAIN);
450             mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1);
451             CatLog.d(LOG_TAG, "slot id: " + mSlotId + ", state: " + mState);
452         } else {
453             CatLog.d(LOG_TAG, "finish!");
454             finish();
455         }
456     }
457 
getSelectedItem(int position)458     private Item getSelectedItem(int position) {
459         Item item = null;
460         if (mStkMenu != null) {
461             try {
462                 item = mStkMenu.items.get(position);
463             } catch (IndexOutOfBoundsException e) {
464                 if (StkApp.DBG) {
465                     CatLog.d(LOG_TAG, "IOOBE Invalid menu");
466                 }
467             } catch (NullPointerException e) {
468                 if (StkApp.DBG) {
469                     CatLog.d(LOG_TAG, "NPE Invalid menu");
470                 }
471             }
472         }
473         return item;
474     }
475 
sendResponse(int resId)476     private void sendResponse(int resId) {
477         sendResponse(resId, 0, false);
478     }
479 
sendResponse(int resId, int itemId, boolean help)480     private void sendResponse(int resId, int itemId, boolean help) {
481         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] itemId[" + itemId +
482             "] help[" + help + "]");
483         mIsResponseSent = true;
484         Bundle args = new Bundle();
485         args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
486         args.putInt(StkAppService.SLOT_ID, mSlotId);
487         args.putInt(StkAppService.RES_ID, resId);
488         args.putInt(StkAppService.MENU_SELECTION, itemId);
489         args.putBoolean(StkAppService.HELP, help);
490         mContext.startService(new Intent(mContext, StkAppService.class)
491                 .putExtras(args));
492     }
493 }
494