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 17package com.android.stk; 18 19import android.app.ActionBar; 20import android.app.Activity; 21import android.content.Context; 22import android.content.Intent; 23import android.graphics.drawable.BitmapDrawable; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.Message; 27import android.text.Editable; 28import android.text.InputFilter; 29import android.text.InputType; 30import android.text.TextWatcher; 31import android.text.method.PasswordTransformationMethod; 32import android.view.KeyEvent; 33import android.view.MenuItem; 34import android.view.View; 35import android.view.Window; 36import android.widget.Button; 37import android.widget.ImageView; 38import android.widget.TextView; 39import android.widget.EditText; 40import android.widget.TextView.BufferType; 41import com.android.internal.telephony.cat.CatLog; 42import com.android.internal.telephony.cat.FontSize; 43import com.android.internal.telephony.cat.Input; 44 45/** 46 * Display a request for a text input a long with a text edit form. 47 */ 48public 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