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