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.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
     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
    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
    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
    174     public void onRestart() {
    175         super.onRestart();
    176         CatLog.d(LOG_TAG, "onRestart, slot id: " + mSlotId);
    177     }
    178 
    179     @Override
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    402     private void cancelTimeOut() {
    403         CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId);
    404         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
    405     }
    406 
    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.
    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 
    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 
    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 
    476     private void sendResponse(int resId) {
    477         sendResponse(resId, 0, false);
    478     }
    479 
    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