• 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.ActionBar;
21 import android.app.Activity;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.support.v4.content.LocalBroadcastManager;
30 import android.telephony.SubscriptionManager;
31 import android.view.ContextMenu;
32 import android.view.ContextMenu.ContextMenuInfo;
33 import android.view.KeyEvent;
34 import android.view.MenuItem;
35 import android.view.View;
36 import android.view.Window;
37 import android.widget.AdapterView;
38 import android.widget.ImageView;
39 import android.widget.ListView;
40 import android.widget.ProgressBar;
41 import android.widget.TextView;
42 
43 import com.android.internal.telephony.cat.Item;
44 import com.android.internal.telephony.cat.Menu;
45 import com.android.internal.telephony.cat.CatLog;
46 import android.telephony.TelephonyManager;
47 
48 /**
49  * ListActivity used for displaying STK menus. These can be SET UP MENU and
50  * SELECT ITEM menus. This activity is started multiple times with different
51  * menu content.
52  *
53  */
54 public class StkMenuActivity extends ListActivity implements View.OnCreateContextMenuListener {
55     private Context mContext;
56     private Menu mStkMenu = null;
57     private int mState = STATE_MAIN;
58     private boolean mAcceptUsersInput = true;
59     private int mSlotId = -1;
60     private boolean mIsResponseSent = false;
61     Activity mInstance = null;
62 
63     private TextView mTitleTextView = null;
64     private ImageView mTitleIconView = null;
65     private ProgressBar mProgressView = null;
66     private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
67     private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
68 
69     private StkAppService appService = StkAppService.getInstance();
70 
71     // Keys for saving the state of the dialog in the bundle
72     private static final String STATE_KEY = "state";
73     private static final String MENU_KEY = "menu";
74     private static final String ACCEPT_USERS_INPUT_KEY = "accept_users_input";
75     private static final String RESPONSE_SENT_KEY = "response_sent";
76 
77     // Internal state values
78     static final int STATE_INIT = 0;
79     static final int STATE_MAIN = 1;
80     static final int STATE_SECONDARY = 2;
81 
82     // Finish result
83     static final int FINISH_CAUSE_NORMAL = 1;
84     static final int FINISH_CAUSE_NULL_SERVICE = 2;
85     static final int FINISH_CAUSE_NULL_MENU = 3;
86 
87     // message id for time out
88     private static final int MSG_ID_TIMEOUT = 1;
89     private static final int CONTEXT_MENU_HELP = 0;
90 
91     Handler mTimeoutHandler = new Handler() {
92         @Override
93         public void handleMessage(Message msg) {
94             switch(msg.what) {
95             case MSG_ID_TIMEOUT:
96                 CatLog.d(LOG_TAG, "MSG_ID_TIMEOUT mState: " + mState);
97                 if (mState == STATE_SECONDARY) {
98                     appService.getStkContext(mSlotId).setPendingActivityInstance(mInstance);
99                 }
100                 sendResponse(StkAppService.RES_ID_TIMEOUT);
101                 //finish();//We wait the following commands to trigger onStop of this activity.
102                 break;
103             }
104         }
105     };
106 
107     @Override
onCreate(Bundle savedInstanceState)108     public void onCreate(Bundle savedInstanceState) {
109         super.onCreate(savedInstanceState);
110 
111         CatLog.d(LOG_TAG, "onCreate");
112 
113         ActionBar actionBar = getActionBar();
114         actionBar.setCustomView(R.layout.stk_title);
115         actionBar.setDisplayShowCustomEnabled(true);
116 
117         // Set the layout for this activity.
118         setContentView(R.layout.stk_menu_list);
119         mInstance = this;
120         mTitleTextView = (TextView) findViewById(R.id.title_text);
121         mTitleIconView = (ImageView) findViewById(R.id.title_icon);
122         mProgressView = (ProgressBar) findViewById(R.id.progress_bar);
123         mContext = getBaseContext();
124         getListView().setOnCreateContextMenuListener(this);
125 
126         // appService can be null if this activity is automatically recreated by the system
127         // with the saved instance state right after the phone process is killed.
128         if (appService == null) {
129             CatLog.d(LOG_TAG, "onCreate - appService is null");
130             finish();
131             return;
132         }
133 
134         LocalBroadcastManager.getInstance(this).registerReceiver(mLocalBroadcastReceiver,
135                 new IntentFilter(StkAppService.SESSION_ENDED));
136         initFromIntent(getIntent());
137         if (!SubscriptionManager.isValidSlotIndex(mSlotId)) {
138             finish();
139             return;
140         }
141     }
142 
143     @Override
onListItemClick(ListView l, View v, int position, long id)144     protected void onListItemClick(ListView l, View v, int position, long id) {
145         super.onListItemClick(l, v, position, id);
146 
147         if (!mAcceptUsersInput) {
148             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
149             return;
150         }
151 
152         Item item = getSelectedItem(position);
153         if (item == null) {
154             CatLog.d(LOG_TAG, "Item is null");
155             return;
156         }
157 
158         CatLog.d(LOG_TAG, "onListItemClick Id: " + item.id + ", mState: " + mState);
159         // ONLY set SECONDARY menu. It will be finished when the following command is comming.
160         if (mState == STATE_SECONDARY) {
161             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
162         }
163         cancelTimeOut();
164         sendResponse(StkAppService.RES_ID_MENU_SELECTION, item.id, false);
165         invalidateOptionsMenu();
166     }
167 
168     @Override
onKeyDown(int keyCode, KeyEvent event)169     public boolean onKeyDown(int keyCode, KeyEvent event) {
170         CatLog.d(LOG_TAG, "mAcceptUsersInput: " + mAcceptUsersInput);
171         if (!mAcceptUsersInput) {
172             return true;
173         }
174 
175         switch (keyCode) {
176         case KeyEvent.KEYCODE_BACK:
177             CatLog.d(LOG_TAG, "KEYCODE_BACK - mState[" + mState + "]");
178             switch (mState) {
179             case STATE_SECONDARY:
180                 CatLog.d(LOG_TAG, "STATE_SECONDARY");
181                 cancelTimeOut();
182                 appService.getStkContext(mSlotId).setPendingActivityInstance(this);
183                 sendResponse(StkAppService.RES_ID_BACKWARD);
184                 return true;
185             case STATE_MAIN:
186                 CatLog.d(LOG_TAG, "STATE_MAIN");
187                 cancelTimeOut();
188                 finish();
189                 return true;
190             }
191             break;
192         }
193         return super.onKeyDown(keyCode, event);
194     }
195 
196     @Override
onRestart()197     public void onRestart() {
198         super.onRestart();
199         CatLog.d(LOG_TAG, "onRestart, slot id: " + mSlotId);
200     }
201 
202     @Override
onResume()203     public void onResume() {
204         super.onResume();
205 
206         CatLog.d(LOG_TAG, "onResume, slot id: " + mSlotId + "," + mState);
207         appService.indicateMenuVisibility(true, mSlotId);
208         if (mState == STATE_MAIN) {
209             mStkMenu = appService.getMainMenu(mSlotId);
210         } else {
211             mStkMenu = appService.getMenu(mSlotId);
212         }
213         if (mStkMenu == null) {
214             CatLog.d(LOG_TAG, "menu is null");
215             cancelTimeOut();
216             finish();
217             return;
218         }
219         displayMenu();
220         startTimeOut();
221         invalidateOptionsMenu();
222     }
223 
224     @Override
onPause()225     public void onPause() {
226         super.onPause();
227         CatLog.d(LOG_TAG, "onPause, slot id: " + mSlotId + "," + mState);
228         //If activity is finished in onResume and it reaults from null appService.
229         if (appService != null) {
230             appService.indicateMenuVisibility(false, mSlotId);
231         } else {
232             CatLog.d(LOG_TAG, "onPause: null appService.");
233         }
234 
235         /*
236          * do not cancel the timer here cancelTimeOut(). If any higher/lower
237          * priority events such as incoming call, new sms, screen off intent,
238          * notification alerts, user actions such as 'User moving to another activtiy'
239          * etc.. occur during SELECT ITEM ongoing session,
240          * this activity would receive 'onPause()' event resulting in
241          * cancellation of the timer. As a result no terminal response is
242          * sent to the card.
243          */
244 
245     }
246 
247     @Override
onStop()248     public void onStop() {
249         super.onStop();
250         CatLog.d(LOG_TAG, "onStop, slot id: " + mSlotId + "," + mIsResponseSent + "," + mState);
251 
252         // Nothing should be done here if this activity is being restarted now.
253         if (isChangingConfigurations()) {
254             return;
255         }
256 
257         //The menu should stay in background, if
258         //1. the dialog is pop up in the screen, but the user does not response to the dialog.
259         //2. the menu activity enters Stop state (e.g pressing HOME key) but mIsResponseSent is false.
260         if (mIsResponseSent) {
261             // ONLY finish SECONDARY menu. MAIN menu should always stay in the root of stack.
262             if (mState == STATE_SECONDARY) {
263                 if (!appService.isStkDialogActivated(mContext)) {
264                     CatLog.d(LOG_TAG, "STATE_SECONDARY finish.");
265                     cancelTimeOut();//To avoid the timer time out and send TR again.
266                     finish();
267                 } else {
268                      if (appService != null) {
269                          appService.getStkContext(mSlotId).setPendingActivityInstance(this);
270                      }
271                 }
272             }
273         } else {
274             if (appService != null) {
275                 if (mState == STATE_SECONDARY) {
276                     appService.getStkContext(mSlotId).setPendingActivityInstance(this);
277                 }
278             } else {
279                 CatLog.d(LOG_TAG, "onStop: null appService.");
280             }
281         }
282     }
283 
284     @Override
onDestroy()285     public void onDestroy() {
286         getListView().setOnCreateContextMenuListener(null);
287         super.onDestroy();
288         CatLog.d(LOG_TAG, "onDestroy" + ", " + mState);
289         if (appService == null || !SubscriptionManager.isValidSlotIndex(mSlotId)) {
290             return;
291         }
292         //isMenuPending: if input act is finish by stkappservice when OP_LAUNCH_APP again,
293         //we can not send TR here, since the input cmd is waiting user to process.
294         if (mState == STATE_SECONDARY && !mIsResponseSent && !appService.isMenuPending(mSlotId)) {
295             // Avoid sending the terminal response while the activty is being restarted
296             // due to some kind of configuration change.
297             if (!isChangingConfigurations()) {
298                 CatLog.d(LOG_TAG, "handleDestroy - Send End Session");
299                 sendResponse(StkAppService.RES_ID_END_SESSION);
300             }
301         }
302         LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalBroadcastReceiver);
303     }
304 
305     @Override
onCreateOptionsMenu(android.view.Menu menu)306     public boolean onCreateOptionsMenu(android.view.Menu menu) {
307         super.onCreateOptionsMenu(menu);
308         menu.add(0, StkApp.MENU_ID_END_SESSION, 1, R.string.menu_end_session);
309         return true;
310     }
311 
312     @Override
onPrepareOptionsMenu(android.view.Menu menu)313     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
314         super.onPrepareOptionsMenu(menu);
315         boolean mainVisible = false;
316 
317         if (mState == STATE_SECONDARY && mAcceptUsersInput) {
318             mainVisible = true;
319         }
320 
321         menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(mainVisible);
322 
323         return mainVisible;
324     }
325 
326     @Override
onOptionsItemSelected(MenuItem item)327     public boolean onOptionsItemSelected(MenuItem item) {
328         if (!mAcceptUsersInput) {
329             return true;
330         }
331         switch (item.getItemId()) {
332         case StkApp.MENU_ID_END_SESSION:
333             cancelTimeOut();
334             // send session end response.
335             sendResponse(StkAppService.RES_ID_END_SESSION);
336             cancelTimeOut();
337             finish();
338             return true;
339         default:
340             break;
341         }
342         return super.onOptionsItemSelected(item);
343     }
344 
345     @Override
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)346     public void onCreateContextMenu(ContextMenu menu, View v,
347             ContextMenuInfo menuInfo) {
348         CatLog.d(this, "onCreateContextMenu");
349         boolean helpVisible = false;
350         if (mStkMenu != null) {
351             helpVisible = mStkMenu.helpAvailable;
352         }
353         if (helpVisible) {
354             CatLog.d(this, "add menu");
355             menu.add(0, CONTEXT_MENU_HELP, 0, R.string.help);
356         }
357     }
358 
359     @Override
onContextItemSelected(MenuItem item)360     public boolean onContextItemSelected(MenuItem item) {
361         AdapterView.AdapterContextMenuInfo info;
362         try {
363             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
364         } catch (ClassCastException e) {
365             return false;
366         }
367         switch (item.getItemId()) {
368             case CONTEXT_MENU_HELP:
369                 cancelTimeOut();
370                 int position = info.position;
371                 CatLog.d(this, "Position:" + position);
372                 Item stkItem = getSelectedItem(position);
373                 if (stkItem != null) {
374                     CatLog.d(this, "item id:" + stkItem.id);
375                     sendResponse(StkAppService.RES_ID_MENU_SELECTION, stkItem.id, true);
376                 }
377                 return true;
378 
379             default:
380                 return super.onContextItemSelected(item);
381         }
382     }
383 
384     @Override
onSaveInstanceState(Bundle outState)385     protected void onSaveInstanceState(Bundle outState) {
386         CatLog.d(LOG_TAG, "onSaveInstanceState: " + mSlotId);
387         outState.putInt(STATE_KEY, mState);
388         outState.putParcelable(MENU_KEY, mStkMenu);
389         outState.putBoolean(ACCEPT_USERS_INPUT_KEY, mAcceptUsersInput);
390         outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent);
391     }
392 
393     @Override
onRestoreInstanceState(Bundle savedInstanceState)394     protected void onRestoreInstanceState(Bundle savedInstanceState) {
395         CatLog.d(LOG_TAG, "onRestoreInstanceState: " + mSlotId);
396         mState = savedInstanceState.getInt(STATE_KEY);
397         mStkMenu = savedInstanceState.getParcelable(MENU_KEY);
398         mAcceptUsersInput = savedInstanceState.getBoolean(ACCEPT_USERS_INPUT_KEY);
399         if (!mAcceptUsersInput) {
400             // Check the latest information as the saved instance state can be outdated.
401             if ((mState == STATE_MAIN) && appService.isMainMenuAvailable(mSlotId)) {
402                 mAcceptUsersInput = true;
403             } else {
404                 showProgressBar(true);
405             }
406         }
407         mIsResponseSent = savedInstanceState.getBoolean(RESPONSE_SENT_KEY);
408     }
409 
cancelTimeOut()410     private void cancelTimeOut() {
411         CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId);
412         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
413     }
414 
startTimeOut()415     private void startTimeOut() {
416         if (mState == STATE_SECONDARY) {
417             // Reset timeout.
418             cancelTimeOut();
419             CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId);
420             mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
421                     .obtainMessage(MSG_ID_TIMEOUT), StkApp.UI_TIMEOUT);
422         }
423     }
424 
425     // Bind list adapter to the items list.
displayMenu()426     private void displayMenu() {
427 
428         if (mStkMenu != null) {
429             String title = mStkMenu.title == null ? getString(R.string.app_name) : mStkMenu.title;
430             // Display title & title icon
431             if (mStkMenu.titleIcon != null) {
432                 mTitleIconView.setImageBitmap(mStkMenu.titleIcon);
433                 mTitleIconView.setVisibility(View.VISIBLE);
434                 mTitleTextView.setVisibility(View.INVISIBLE);
435                 if (!mStkMenu.titleIconSelfExplanatory) {
436                     mTitleTextView.setText(title);
437                     mTitleTextView.setVisibility(View.VISIBLE);
438                 }
439             } else {
440                 mTitleIconView.setVisibility(View.GONE);
441                 mTitleTextView.setVisibility(View.VISIBLE);
442                 mTitleTextView.setText(title);
443             }
444             // create an array adapter for the menu list
445             StkMenuAdapter adapter = new StkMenuAdapter(this,
446                     mStkMenu.items, mStkMenu.itemsIconSelfExplanatory);
447             // Bind menu list to the new adapter.
448             setListAdapter(adapter);
449             // Set default item
450             setSelection(mStkMenu.defaultItem);
451         }
452     }
453 
showProgressBar(boolean show)454     private void showProgressBar(boolean show) {
455         if (show) {
456             mProgressView.setIndeterminate(true);
457             mProgressView.setVisibility(View.VISIBLE);
458         } else {
459             mProgressView.setIndeterminate(false);
460             mProgressView.setVisibility(View.GONE);
461         }
462     }
463 
initFromIntent(Intent intent)464     private void initFromIntent(Intent intent) {
465 
466         if (intent != null) {
467             mState = intent.getIntExtra("STATE", STATE_MAIN);
468             mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1);
469             CatLog.d(LOG_TAG, "slot id: " + mSlotId + ", state: " + mState);
470         } else {
471             CatLog.d(LOG_TAG, "finish!");
472             finish();
473         }
474     }
475 
getSelectedItem(int position)476     private Item getSelectedItem(int position) {
477         Item item = null;
478         if (mStkMenu != null) {
479             try {
480                 item = mStkMenu.items.get(position);
481             } catch (IndexOutOfBoundsException e) {
482                 if (StkApp.DBG) {
483                     CatLog.d(LOG_TAG, "IOOBE Invalid menu");
484                 }
485             } catch (NullPointerException e) {
486                 if (StkApp.DBG) {
487                     CatLog.d(LOG_TAG, "NPE Invalid menu");
488                 }
489             }
490         }
491         return item;
492     }
493 
sendResponse(int resId)494     private void sendResponse(int resId) {
495         sendResponse(resId, 0, false);
496     }
497 
sendResponse(int resId, int itemId, boolean help)498     private void sendResponse(int resId, int itemId, boolean help) {
499         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] itemId[" + itemId +
500             "] help[" + help + "]");
501 
502         // Disallow user operation temporarily until receiving the result of the response.
503         mAcceptUsersInput = false;
504         if (resId == StkAppService.RES_ID_MENU_SELECTION) {
505             showProgressBar(true);
506         }
507 
508         mIsResponseSent = true;
509         Bundle args = new Bundle();
510         args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
511         args.putInt(StkAppService.SLOT_ID, mSlotId);
512         args.putInt(StkAppService.RES_ID, resId);
513         args.putInt(StkAppService.MENU_SELECTION, itemId);
514         args.putBoolean(StkAppService.HELP, help);
515         mContext.startService(new Intent(mContext, StkAppService.class)
516                 .putExtras(args));
517     }
518 
519     private final BroadcastReceiver mLocalBroadcastReceiver = new BroadcastReceiver() {
520         @Override
521         public void onReceive(Context context, Intent intent) {
522             if (StkAppService.SESSION_ENDED.equals(intent.getAction())) {
523                 int slotId = intent.getIntExtra(StkAppService.SLOT_ID, 0);
524                 if ((mState == STATE_MAIN) && (mSlotId == slotId)) {
525                     mAcceptUsersInput = true;
526                     showProgressBar(false);
527                 }
528             }
529         }
530     };
531 }
532