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"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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.ActionBar;
     20 import android.app.Activity;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.graphics.drawable.BitmapDrawable;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.text.Editable;
     28 import android.text.InputFilter;
     29 import android.text.InputType;
     30 import android.text.TextWatcher;
     31 import android.text.method.PasswordTransformationMethod;
     32 import android.view.KeyEvent;
     33 import android.view.MenuItem;
     34 import android.view.View;
     35 import android.view.Window;
     36 import android.widget.Button;
     37 import android.widget.ImageView;
     38 import android.widget.TextView;
     39 import android.widget.EditText;
     40 import android.widget.TextView.BufferType;
     41 import com.android.internal.telephony.cat.CatLog;
     42 import com.android.internal.telephony.cat.FontSize;
     43 import com.android.internal.telephony.cat.Input;
     44 
     45 /**
     46  * Display a request for a text input a long with a text edit form.
     47  */
     48 public class StkInputActivity extends Activity implements View.OnClickListener,
     49         TextWatcher {
     50 
     51     // Members
     52     private int mState;
     53     private Context mContext;
     54     private EditText mTextIn = null;
     55     private TextView mPromptView = null;
     56     private View mYesNoLayout = null;
     57     private View mNormalLayout = null;
     58 
     59     // Constants
     60     private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
     61     private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
     62 
     63     private Input mStkInput = null;
     64     private boolean mAcceptUsersInput = true;
     65     // Constants
     66     private static final int STATE_TEXT = 1;
     67     private static final int STATE_YES_NO = 2;
     68 
     69     static final String YES_STR_RESPONSE = "YES";
     70     static final String NO_STR_RESPONSE = "NO";
     71 
     72     // Font size factor values.
     73     static final float NORMAL_FONT_FACTOR = 1;
     74     static final float LARGE_FONT_FACTOR = 2;
     75     static final float SMALL_FONT_FACTOR = (1 / 2);
     76 
     77     // message id for time out
     78     private static final int MSG_ID_TIMEOUT = 1;
     79     private StkAppService appService = StkAppService.getInstance();
     80 
     81     private boolean mIsResponseSent = false;
     82     private int mSlotId = -1;
     83     Activity mInstance = null;
     84 
     85     Handler mTimeoutHandler = new Handler() {
     86         @Override
     87         public void handleMessage(Message msg) {
     88             switch(msg.what) {
     89             case MSG_ID_TIMEOUT:
     90                 CatLog.d(LOG_TAG, "Msg timeout.");
     91                 mAcceptUsersInput = false;
     92                 appService.getStkContext(mSlotId).setPendingActivityInstance(mInstance);
     93                 sendResponse(StkAppService.RES_ID_TIMEOUT);
     94                 break;
     95             }
     96         }
     97     };
     98 
     99     // Click listener to handle buttons press..
    100     public void onClick(View v) {
    101         String input = null;
    102         if (!mAcceptUsersInput) {
    103             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
    104             return;
    105         }
    106 
    107         switch (v.getId()) {
    108         case R.id.button_ok:
    109             // Check that text entered is valid .
    110             if (!verfiyTypedText()) {
    111                 CatLog.d(LOG_TAG, "handleClick, invalid text");
    112                 return;
    113             }
    114             mAcceptUsersInput = false;
    115             input = mTextIn.getText().toString();
    116             break;
    117         // Yes/No layout buttons.
    118         case R.id.button_yes:
    119             mAcceptUsersInput = false;
    120             input = YES_STR_RESPONSE;
    121             break;
    122         case R.id.button_no:
    123             mAcceptUsersInput = false;
    124             input = NO_STR_RESPONSE;
    125             break;
    126         }
    127         CatLog.d(LOG_TAG, "handleClick, ready to response");
    128         cancelTimeOut();
    129         appService.getStkContext(mSlotId).setPendingActivityInstance(this);
    130         sendResponse(StkAppService.RES_ID_INPUT, input, false);
    131     }
    132 
    133     @Override
    134     public void onCreate(Bundle icicle) {
    135         super.onCreate(icicle);
    136 
    137         CatLog.d(LOG_TAG, "onCreate - mIsResponseSent[" + mIsResponseSent + "]");
    138 
    139         // appService can be null if this activity is automatically recreated by the system
    140         // with the saved instance state right after the phone process is killed.
    141         if (appService == null) {
    142             CatLog.d(LOG_TAG, "onCreate - appService is null");
    143             finish();
    144             return;
    145         }
    146 
    147         ActionBar actionBar = getActionBar();
    148         actionBar.setCustomView(R.layout.stk_title);
    149         actionBar.setDisplayShowCustomEnabled(true);
    150 
    151         // Set the layout for this activity.
    152         setContentView(R.layout.stk_input);
    153 
    154         // Initialize members
    155         mTextIn = (EditText) this.findViewById(R.id.in_text);
    156         mPromptView = (TextView) this.findViewById(R.id.prompt);
    157         mInstance = this;
    158         // Set buttons listeners.
    159         Button okButton = (Button) findViewById(R.id.button_ok);
    160         Button yesButton = (Button) findViewById(R.id.button_yes);
    161         Button noButton = (Button) findViewById(R.id.button_no);
    162 
    163         okButton.setOnClickListener(this);
    164         yesButton.setOnClickListener(this);
    165         noButton.setOnClickListener(this);
    166 
    167         mYesNoLayout = findViewById(R.id.yes_no_layout);
    168         mNormalLayout = findViewById(R.id.normal_layout);
    169         initFromIntent(getIntent());
    170         mContext = getBaseContext();
    171         mAcceptUsersInput = true;
    172     }
    173 
    174     @Override
    175     protected void onPostCreate(Bundle savedInstanceState) {
    176         super.onPostCreate(savedInstanceState);
    177 
    178         mTextIn.addTextChangedListener(this);
    179     }
    180 
    181     @Override
    182     public void onResume() {
    183         super.onResume();
    184         CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
    185                 "], slot id: " + mSlotId);
    186         startTimeOut();
    187         appService.getStkContext(mSlotId).setPendingActivityInstance(null);
    188         if (mIsResponseSent) {
    189             cancelTimeOut();
    190             finish();
    191         }
    192     }
    193 
    194     @Override
    195     public void onPause() {
    196         super.onPause();
    197         CatLog.d(LOG_TAG, "onPause - mIsResponseSent[" + mIsResponseSent + "]");
    198     }
    199 
    200     @Override
    201     public void onStop() {
    202         super.onStop();
    203         CatLog.d(LOG_TAG, "onStop - mIsResponseSent[" + mIsResponseSent + "]");
    204         if (mIsResponseSent) {
    205             cancelTimeOut();
    206             finish();
    207         } else {
    208             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
    209         }
    210     }
    211 
    212     @Override
    213     public void onDestroy() {
    214         super.onDestroy();
    215         CatLog.d(LOG_TAG, "onDestroy - before Send End Session mIsResponseSent[" +
    216                 mIsResponseSent + " , " + mSlotId + "]");
    217         if (appService == null) {
    218             return;
    219         }
    220         //If the input activity is finished by stkappservice
    221         //when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here
    222         //, since the input cmd is waiting user to process.
    223         if (!mIsResponseSent && !appService.isInputPending(mSlotId)) {
    224             CatLog.d(LOG_TAG, "handleDestroy - Send End Session");
    225             sendResponse(StkAppService.RES_ID_END_SESSION);
    226         }
    227         cancelTimeOut();
    228     }
    229 
    230     @Override
    231     public boolean onKeyDown(int keyCode, KeyEvent event) {
    232         if (!mAcceptUsersInput) {
    233             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
    234             return true;
    235         }
    236 
    237         switch (keyCode) {
    238         case KeyEvent.KEYCODE_BACK:
    239             CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK");
    240             mAcceptUsersInput = false;
    241             cancelTimeOut();
    242             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
    243             sendResponse(StkAppService.RES_ID_BACKWARD, null, false);
    244             return true;
    245         }
    246         return super.onKeyDown(keyCode, event);
    247     }
    248 
    249     void sendResponse(int resId) {
    250         sendResponse(resId, null, false);
    251     }
    252 
    253     void sendResponse(int resId, String input, boolean help) {
    254         if (mSlotId == -1) {
    255             CatLog.d(LOG_TAG, "slot id is invalid");
    256             return;
    257         }
    258 
    259         if (StkAppService.getInstance() == null) {
    260             CatLog.d(LOG_TAG, "StkAppService is null, Ignore response: id is " + resId);
    261             return;
    262         }
    263 
    264         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] input[*****] help["
    265                 + help + "]");
    266         mIsResponseSent = true;
    267         Bundle args = new Bundle();
    268         args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
    269         args.putInt(StkAppService.SLOT_ID, mSlotId);
    270         args.putInt(StkAppService.RES_ID, resId);
    271         if (input != null) {
    272             args.putString(StkAppService.INPUT, input);
    273         }
    274         args.putBoolean(StkAppService.HELP, help);
    275         mContext.startService(new Intent(mContext, StkAppService.class)
    276                 .putExtras(args));
    277     }
    278 
    279     @Override
    280     public boolean onCreateOptionsMenu(android.view.Menu menu) {
    281         super.onCreateOptionsMenu(menu);
    282         menu.add(android.view.Menu.NONE, StkApp.MENU_ID_END_SESSION, 1,
    283                 R.string.menu_end_session);
    284         menu.add(0, StkApp.MENU_ID_HELP, 2, R.string.help);
    285 
    286         return true;
    287     }
    288 
    289     @Override
    290     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
    291         super.onPrepareOptionsMenu(menu);
    292         menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(true);
    293         menu.findItem(StkApp.MENU_ID_HELP).setVisible(mStkInput.helpAvailable);
    294 
    295         return true;
    296     }
    297 
    298     @Override
    299     public boolean onOptionsItemSelected(MenuItem item) {
    300         if (!mAcceptUsersInput) {
    301             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
    302             return true;
    303         }
    304         switch (item.getItemId()) {
    305         case StkApp.MENU_ID_END_SESSION:
    306             mAcceptUsersInput = false;
    307             cancelTimeOut();
    308             sendResponse(StkAppService.RES_ID_END_SESSION);
    309             finish();
    310             return true;
    311         case StkApp.MENU_ID_HELP:
    312             mAcceptUsersInput = false;
    313             cancelTimeOut();
    314             sendResponse(StkAppService.RES_ID_INPUT, "", true);
    315             finish();
    316             return true;
    317         }
    318         return super.onOptionsItemSelected(item);
    319     }
    320 
    321     @Override
    322     protected void onSaveInstanceState(Bundle outState) {
    323         CatLog.d(LOG_TAG, "onSaveInstanceState: " + mSlotId);
    324         outState.putBoolean("ACCEPT_USERS_INPUT", mAcceptUsersInput);
    325     }
    326 
    327     @Override
    328     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    329         CatLog.d(LOG_TAG, "onRestoreInstanceState: " + mSlotId);
    330         mAcceptUsersInput = savedInstanceState.getBoolean("ACCEPT_USERS_INPUT");
    331     }
    332 
    333     public void beforeTextChanged(CharSequence s, int start, int count,
    334             int after) {
    335     }
    336 
    337     public void onTextChanged(CharSequence s, int start, int before, int count) {
    338         // Reset timeout.
    339         startTimeOut();
    340     }
    341 
    342     public void afterTextChanged(Editable s) {
    343     }
    344 
    345     private boolean verfiyTypedText() {
    346         // If not enough input was typed in stay on the edit screen.
    347         if (mTextIn.getText().length() < mStkInput.minLen) {
    348             return false;
    349         }
    350 
    351         return true;
    352     }
    353 
    354     private void cancelTimeOut() {
    355         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
    356     }
    357 
    358     private void startTimeOut() {
    359         int duration = StkApp.calculateDurationInMilis(mStkInput.duration);
    360 
    361         if (duration <= 0) {
    362             duration = StkApp.UI_TIMEOUT;
    363         }
    364         cancelTimeOut();
    365         mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
    366                 .obtainMessage(MSG_ID_TIMEOUT), duration);
    367     }
    368 
    369     private void configInputDisplay() {
    370         TextView numOfCharsView = (TextView) findViewById(R.id.num_of_chars);
    371         TextView inTypeView = (TextView) findViewById(R.id.input_type);
    372 
    373         int inTypeId = R.string.alphabet;
    374 
    375         // set the prompt.
    376         if (mStkInput.iconSelfExplanatory && mStkInput.icon != null) {
    377             mPromptView.setVisibility(View.GONE);
    378         } else {
    379             mPromptView.setText(mStkInput.text);
    380         }
    381 
    382         // Set input type (alphabet/digit) info close to the InText form.
    383         if (mStkInput.digitOnly) {
    384             mTextIn.setKeyListener(StkDigitsKeyListener.getInstance());
    385             inTypeId = R.string.digits;
    386         }
    387         inTypeView.setText(inTypeId);
    388 
    389         TextView textView = (TextView) this.findViewById(R.id.title_text);
    390         textView.setText(R.string.app_name);
    391 
    392         if (mStkInput.icon != null) {
    393             ImageView imageView = (ImageView) findViewById(R.id.title_icon);
    394             imageView.setImageBitmap(mStkInput.icon);
    395             imageView.setVisibility(View.VISIBLE);
    396         }
    397 
    398         // Handle specific global and text attributes.
    399         switch (mState) {
    400         case STATE_TEXT:
    401             int maxLen = mStkInput.maxLen;
    402             int minLen = mStkInput.minLen;
    403             mTextIn.setFilters(new InputFilter[] {new InputFilter.LengthFilter(
    404                     maxLen)});
    405 
    406             // Set number of chars info.
    407             String lengthLimit = String.valueOf(minLen);
    408             if (maxLen != minLen) {
    409                 lengthLimit = minLen + " - " + maxLen;
    410             }
    411             numOfCharsView.setText(lengthLimit);
    412 
    413             if (!mStkInput.echo) {
    414                 mTextIn.setInputType(InputType.TYPE_CLASS_NUMBER
    415                                      | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
    416             }
    417             // Set default text if present.
    418             if (mStkInput.defaultText != null) {
    419                 mTextIn.setText(mStkInput.defaultText);
    420             } else {
    421                 // make sure the text is cleared
    422                 mTextIn.setText("", BufferType.EDITABLE);
    423             }
    424 
    425             break;
    426         case STATE_YES_NO:
    427             // Set display mode - normal / yes-no layout
    428             mYesNoLayout.setVisibility(View.VISIBLE);
    429             mNormalLayout.setVisibility(View.GONE);
    430             break;
    431         }
    432     }
    433 
    434     private float getFontSizeFactor(FontSize size) {
    435         final float[] fontSizes =
    436             {NORMAL_FONT_FACTOR, LARGE_FONT_FACTOR, SMALL_FONT_FACTOR};
    437 
    438         return fontSizes[size.ordinal()];
    439     }
    440 
    441     private void initFromIntent(Intent intent) {
    442         // Get the calling intent type: text/key, and setup the
    443         // display parameters.
    444         CatLog.d(LOG_TAG, "initFromIntent - slot id: " + mSlotId);
    445         if (intent != null) {
    446             mStkInput = intent.getParcelableExtra("INPUT");
    447             mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1);
    448             CatLog.d(LOG_TAG, "onCreate - slot id: " + mSlotId);
    449             if (mStkInput == null) {
    450                 finish();
    451             } else {
    452                 mState = mStkInput.yesNo ? STATE_YES_NO :
    453                         STATE_TEXT;
    454                 configInputDisplay();
    455             }
    456         } else {
    457             finish();
    458         }
    459     }
    460 }
    461