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