• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.res.Configuration;
24 import android.graphics.drawable.BitmapDrawable;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.text.Editable;
29 import android.text.InputFilter;
30 import android.text.InputType;
31 import android.text.TextUtils;
32 import android.text.TextWatcher;
33 import android.text.method.PasswordTransformationMethod;
34 import android.view.inputmethod.EditorInfo;
35 import android.view.KeyEvent;
36 import android.view.Menu;
37 import android.view.MenuItem;
38 import android.view.View;
39 import android.view.Window;
40 import android.widget.Button;
41 import android.widget.ImageView;
42 import android.widget.PopupMenu;
43 import android.widget.TextView;
44 import android.widget.EditText;
45 import android.widget.TextView.BufferType;
46 import com.android.internal.telephony.cat.CatLog;
47 import com.android.internal.telephony.cat.FontSize;
48 import com.android.internal.telephony.cat.Input;
49 
50 /**
51  * Display a request for a text input a long with a text edit form.
52  */
53 public class StkInputActivity extends Activity implements View.OnClickListener,
54         TextWatcher {
55 
56     // Members
57     private int mState;
58     private Context mContext;
59     private EditText mTextIn = null;
60     private TextView mPromptView = null;
61     private View mMoreOptions = null;
62     private PopupMenu mPopupMenu = null;
63     private View mYesNoLayout = null;
64     private View mNormalLayout = null;
65 
66     // Constants
67     private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
68     private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
69 
70     private Input mStkInput = null;
71     private boolean mAcceptUsersInput = true;
72     // Constants
73     private static final int STATE_TEXT = 1;
74     private static final int STATE_YES_NO = 2;
75 
76     static final String YES_STR_RESPONSE = "YES";
77     static final String NO_STR_RESPONSE = "NO";
78 
79     // Font size factor values.
80     static final float NORMAL_FONT_FACTOR = 1;
81     static final float LARGE_FONT_FACTOR = 2;
82     static final float SMALL_FONT_FACTOR = (1 / 2);
83 
84     // Keys for saving the state of the activity in the bundle
85     private static final String ACCEPT_USERS_INPUT_KEY = "accept_users_input";
86     private static final String RESPONSE_SENT_KEY = "response_sent";
87     private static final String INPUT_STRING_KEY = "input_string";
88 
89     // message id for time out
90     private static final int MSG_ID_TIMEOUT = 1;
91     private StkAppService appService = StkAppService.getInstance();
92 
93     private boolean mIsResponseSent = false;
94     private int mSlotId = -1;
95     Activity mInstance = null;
96 
97     Handler mTimeoutHandler = new Handler() {
98         @Override
99         public void handleMessage(Message msg) {
100             switch(msg.what) {
101             case MSG_ID_TIMEOUT:
102                 CatLog.d(LOG_TAG, "Msg timeout.");
103                 mAcceptUsersInput = false;
104                 appService.getStkContext(mSlotId).setPendingActivityInstance(mInstance);
105                 sendResponse(StkAppService.RES_ID_TIMEOUT);
106                 break;
107             }
108         }
109     };
110 
111     // Click listener to handle buttons press..
onClick(View v)112     public void onClick(View v) {
113         String input = null;
114         if (!mAcceptUsersInput) {
115             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
116             return;
117         }
118 
119         switch (v.getId()) {
120         case R.id.button_ok:
121             // Check that text entered is valid .
122             if (!verfiyTypedText()) {
123                 CatLog.d(LOG_TAG, "handleClick, invalid text");
124                 return;
125             }
126             mAcceptUsersInput = false;
127             input = mTextIn.getText().toString();
128             break;
129         case R.id.button_cancel:
130             mAcceptUsersInput = false;
131             cancelTimeOut();
132             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
133             sendResponse(StkAppService.RES_ID_END_SESSION);
134             return;
135         // Yes/No layout buttons.
136         case R.id.button_yes:
137             mAcceptUsersInput = false;
138             input = YES_STR_RESPONSE;
139             break;
140         case R.id.button_no:
141             mAcceptUsersInput = false;
142             input = NO_STR_RESPONSE;
143             break;
144         case R.id.more:
145             if (mPopupMenu == null) {
146                 mPopupMenu = new PopupMenu(this, v);
147                 Menu menu = mPopupMenu.getMenu();
148                 createOptionsMenuInternal(menu);
149                 prepareOptionsMenuInternal(menu);
150                 mPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
151                     public boolean onMenuItemClick(MenuItem item) {
152                         optionsItemSelectedInternal(item);
153                         return true;
154                     }
155                 });
156                 mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
157                     public void onDismiss(PopupMenu menu) {
158                         mPopupMenu = null;
159                     }
160                 });
161                 mPopupMenu.show();
162             }
163             return;
164         default:
165             break;
166         }
167         CatLog.d(LOG_TAG, "handleClick, ready to response");
168         cancelTimeOut();
169         appService.getStkContext(mSlotId).setPendingActivityInstance(this);
170         sendResponse(StkAppService.RES_ID_INPUT, input, false);
171     }
172 
173     @Override
onCreate(Bundle savedInstanceState)174     public void onCreate(Bundle savedInstanceState) {
175         super.onCreate(savedInstanceState);
176 
177         CatLog.d(LOG_TAG, "onCreate - mIsResponseSent[" + mIsResponseSent + "]");
178 
179         // appService can be null if this activity is automatically recreated by the system
180         // with the saved instance state right after the phone process is killed.
181         if (appService == null) {
182             CatLog.d(LOG_TAG, "onCreate - appService is null");
183             finish();
184             return;
185         }
186 
187         ActionBar actionBar = null;
188         if (getResources().getBoolean(R.bool.show_menu_title_only_on_menu)) {
189             actionBar = getActionBar();
190             if (actionBar != null) {
191                 actionBar.hide();
192             }
193         }
194 
195         // Set the layout for this activity.
196         setContentView(R.layout.stk_input);
197 
198         if (actionBar != null) {
199             mMoreOptions = findViewById(R.id.more);
200             mMoreOptions.setVisibility(View.VISIBLE);
201             mMoreOptions.setOnClickListener(this);
202         }
203 
204         // Initialize members
205         mTextIn = (EditText) this.findViewById(R.id.in_text);
206         mPromptView = (TextView) this.findViewById(R.id.prompt);
207         mInstance = this;
208         // Set buttons listeners.
209         Button okButton = (Button) findViewById(R.id.button_ok);
210         Button cancelButton = (Button) findViewById(R.id.button_cancel);
211         Button yesButton = (Button) findViewById(R.id.button_yes);
212         Button noButton = (Button) findViewById(R.id.button_no);
213 
214         okButton.setOnClickListener(this);
215         cancelButton.setOnClickListener(this);
216         yesButton.setOnClickListener(this);
217         noButton.setOnClickListener(this);
218 
219         mYesNoLayout = findViewById(R.id.yes_no_layout);
220         mNormalLayout = findViewById(R.id.normal_layout);
221         initFromIntent(getIntent());
222         mContext = getBaseContext();
223         mAcceptUsersInput = true;
224     }
225 
226     @Override
onPostCreate(Bundle savedInstanceState)227     protected void onPostCreate(Bundle savedInstanceState) {
228         super.onPostCreate(savedInstanceState);
229 
230         mTextIn.addTextChangedListener(this);
231     }
232 
233     @Override
onResume()234     public void onResume() {
235         super.onResume();
236         CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
237                 "], slot id: " + mSlotId);
238         startTimeOut();
239     }
240 
241     @Override
onPause()242     public void onPause() {
243         super.onPause();
244         CatLog.d(LOG_TAG, "onPause - mIsResponseSent[" + mIsResponseSent + "]");
245         if (mPopupMenu != null) {
246             mPopupMenu.dismiss();
247         }
248     }
249 
250     @Override
onStop()251     public void onStop() {
252         super.onStop();
253         CatLog.d(LOG_TAG, "onStop - mIsResponseSent[" + mIsResponseSent + "]");
254 
255         // Nothing should be done here if this activity is being restarted now.
256         if (isChangingConfigurations()) {
257             return;
258         }
259 
260         // It is unnecessary to keep this activity if the response was already sent and
261         // this got invisible because of the other full-screen activity in this application.
262         if (mIsResponseSent && appService.isTopOfStack()) {
263             cancelTimeOut();
264             finish();
265         } else {
266             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
267         }
268     }
269 
270     @Override
onDestroy()271     public void onDestroy() {
272         super.onDestroy();
273         CatLog.d(LOG_TAG, "onDestroy - before Send End Session mIsResponseSent[" +
274                 mIsResponseSent + " , " + mSlotId + "]");
275         if (appService == null) {
276             return;
277         }
278         // Avoid sending the terminal response while the activty is being restarted
279         // due to some kind of configuration change.
280         if (!isChangingConfigurations()) {
281             // If the input activity is finished by stkappservice
282             // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here,
283             // since the input cmd is waiting user to process.
284             if (!mIsResponseSent && !appService.isInputPending(mSlotId)) {
285                 CatLog.d(LOG_TAG, "handleDestroy - Send End Session");
286                 sendResponse(StkAppService.RES_ID_END_SESSION);
287             }
288             cancelTimeOut();
289         }
290     }
291 
292     @Override
onConfigurationChanged(Configuration newConfig)293     public void onConfigurationChanged(Configuration newConfig) {
294         super.onConfigurationChanged(newConfig);
295         if (mPopupMenu != null) {
296             mPopupMenu.dismiss();
297         }
298     }
299 
300     @Override
onKeyDown(int keyCode, KeyEvent event)301     public boolean onKeyDown(int keyCode, KeyEvent event) {
302         if (!mAcceptUsersInput) {
303             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
304             return true;
305         }
306 
307         switch (keyCode) {
308         case KeyEvent.KEYCODE_BACK:
309             CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK");
310             mAcceptUsersInput = false;
311             cancelTimeOut();
312             appService.getStkContext(mSlotId).setPendingActivityInstance(this);
313             sendResponse(StkAppService.RES_ID_BACKWARD, null, false);
314             return true;
315         }
316         return super.onKeyDown(keyCode, event);
317     }
318 
sendResponse(int resId)319     void sendResponse(int resId) {
320         sendResponse(resId, null, false);
321     }
322 
sendResponse(int resId, String input, boolean help)323     void sendResponse(int resId, String input, boolean help) {
324         if (mSlotId == -1) {
325             CatLog.d(LOG_TAG, "slot id is invalid");
326             return;
327         }
328 
329         if (StkAppService.getInstance() == null) {
330             CatLog.d(LOG_TAG, "StkAppService is null, Ignore response: id is " + resId);
331             return;
332         }
333 
334         if (mMoreOptions != null) {
335             mMoreOptions.setVisibility(View.INVISIBLE);
336         }
337 
338         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] input[*****] help["
339                 + help + "]");
340         mIsResponseSent = true;
341         Bundle args = new Bundle();
342         args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
343         args.putInt(StkAppService.SLOT_ID, mSlotId);
344         args.putInt(StkAppService.RES_ID, resId);
345         if (input != null) {
346             args.putString(StkAppService.INPUT, input);
347         }
348         args.putBoolean(StkAppService.HELP, help);
349         mContext.startService(new Intent(mContext, StkAppService.class)
350                 .putExtras(args));
351     }
352 
353     @Override
onCreateOptionsMenu(android.view.Menu menu)354     public boolean onCreateOptionsMenu(android.view.Menu menu) {
355         super.onCreateOptionsMenu(menu);
356         createOptionsMenuInternal(menu);
357         return true;
358     }
359 
createOptionsMenuInternal(Menu menu)360     private void createOptionsMenuInternal(Menu menu) {
361         menu.add(Menu.NONE, StkApp.MENU_ID_END_SESSION, 1, R.string.menu_end_session);
362         menu.add(0, StkApp.MENU_ID_HELP, 2, R.string.help);
363     }
364 
365     @Override
onPrepareOptionsMenu(android.view.Menu menu)366     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
367         super.onPrepareOptionsMenu(menu);
368         prepareOptionsMenuInternal(menu);
369         return true;
370     }
371 
prepareOptionsMenuInternal(Menu menu)372     private void prepareOptionsMenuInternal(Menu menu) {
373         menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(true);
374         menu.findItem(StkApp.MENU_ID_HELP).setVisible(mStkInput.helpAvailable);
375     }
376 
377     @Override
onOptionsItemSelected(MenuItem item)378     public boolean onOptionsItemSelected(MenuItem item) {
379         if (optionsItemSelectedInternal(item)) {
380             return true;
381         }
382         return super.onOptionsItemSelected(item);
383     }
384 
optionsItemSelectedInternal(MenuItem item)385     private boolean optionsItemSelectedInternal(MenuItem item) {
386         if (!mAcceptUsersInput) {
387             CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
388             return true;
389         }
390         switch (item.getItemId()) {
391         case StkApp.MENU_ID_END_SESSION:
392             mAcceptUsersInput = false;
393             cancelTimeOut();
394             sendResponse(StkAppService.RES_ID_END_SESSION);
395             finish();
396             return true;
397         case StkApp.MENU_ID_HELP:
398             mAcceptUsersInput = false;
399             cancelTimeOut();
400             sendResponse(StkAppService.RES_ID_INPUT, "", true);
401             finish();
402             return true;
403         }
404         return false;
405     }
406 
407     @Override
onSaveInstanceState(Bundle outState)408     protected void onSaveInstanceState(Bundle outState) {
409         CatLog.d(LOG_TAG, "onSaveInstanceState: " + mSlotId);
410         outState.putBoolean(ACCEPT_USERS_INPUT_KEY, mAcceptUsersInput);
411         outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent);
412         outState.putString(INPUT_STRING_KEY, mTextIn.getText().toString());
413     }
414 
415     @Override
onRestoreInstanceState(Bundle savedInstanceState)416     protected void onRestoreInstanceState(Bundle savedInstanceState) {
417         CatLog.d(LOG_TAG, "onRestoreInstanceState: " + mSlotId);
418 
419         mAcceptUsersInput = savedInstanceState.getBoolean(ACCEPT_USERS_INPUT_KEY);
420         if ((mAcceptUsersInput == false) && (mMoreOptions != null)) {
421             mMoreOptions.setVisibility(View.INVISIBLE);
422         }
423 
424         mIsResponseSent = savedInstanceState.getBoolean(RESPONSE_SENT_KEY);
425 
426         String savedString = savedInstanceState.getString(INPUT_STRING_KEY);
427         if (!TextUtils.isEmpty(savedString)) {
428             mTextIn.setText(savedString);
429         }
430     }
431 
beforeTextChanged(CharSequence s, int start, int count, int after)432     public void beforeTextChanged(CharSequence s, int start, int count,
433             int after) {
434     }
435 
onTextChanged(CharSequence s, int start, int before, int count)436     public void onTextChanged(CharSequence s, int start, int before, int count) {
437         // Reset timeout.
438         startTimeOut();
439     }
440 
afterTextChanged(Editable s)441     public void afterTextChanged(Editable s) {
442     }
443 
verfiyTypedText()444     private boolean verfiyTypedText() {
445         // If not enough input was typed in stay on the edit screen.
446         if (mTextIn.getText().length() < mStkInput.minLen) {
447             return false;
448         }
449 
450         return true;
451     }
452 
cancelTimeOut()453     private void cancelTimeOut() {
454         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
455     }
456 
startTimeOut()457     private void startTimeOut() {
458         int duration = StkApp.calculateDurationInMilis(mStkInput.duration);
459 
460         if (duration <= 0) {
461             duration = StkApp.UI_TIMEOUT;
462         }
463         cancelTimeOut();
464         mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
465                 .obtainMessage(MSG_ID_TIMEOUT), duration);
466     }
467 
configInputDisplay()468     private void configInputDisplay() {
469         TextView numOfCharsView = (TextView) findViewById(R.id.num_of_chars);
470         TextView inTypeView = (TextView) findViewById(R.id.input_type);
471 
472         int inTypeId = R.string.alphabet;
473 
474         // set the prompt.
475         if ((mStkInput.icon == null || !mStkInput.iconSelfExplanatory)
476                 && !TextUtils.isEmpty(mStkInput.text)) {
477             mPromptView.setText(mStkInput.text);
478             mPromptView.setVisibility(View.VISIBLE);
479         }
480 
481         // Set input type (alphabet/digit) info close to the InText form.
482         if (mStkInput.digitOnly) {
483             mTextIn.setKeyListener(StkDigitsKeyListener.getInstance());
484             inTypeId = R.string.digits;
485         }
486         inTypeView.setText(inTypeId);
487 
488         setTitle(R.string.app_name);
489 
490         if (mStkInput.icon != null) {
491             ImageView imageView = (ImageView) findViewById(R.id.icon);
492             imageView.setImageBitmap(mStkInput.icon);
493             imageView.setVisibility(View.VISIBLE);
494         }
495 
496         // Handle specific global and text attributes.
497         switch (mState) {
498         case STATE_TEXT:
499             int maxLen = mStkInput.maxLen;
500             int minLen = mStkInput.minLen;
501             mTextIn.setFilters(new InputFilter[] {new InputFilter.LengthFilter(
502                     maxLen)});
503 
504             // Set number of chars info.
505             String lengthLimit = String.valueOf(minLen);
506             if (maxLen != minLen) {
507                 lengthLimit = minLen + " - " + maxLen;
508             }
509             numOfCharsView.setText(lengthLimit);
510 
511             if (!mStkInput.echo) {
512                 mTextIn.setTransformationMethod(PasswordTransformationMethod
513                         .getInstance());
514             }
515             mTextIn.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN);
516             // Set default text if present.
517             if (mStkInput.defaultText != null) {
518                 mTextIn.setText(mStkInput.defaultText);
519             } else {
520                 // make sure the text is cleared
521                 mTextIn.setText("", BufferType.EDITABLE);
522             }
523 
524             break;
525         case STATE_YES_NO:
526             // Set display mode - normal / yes-no layout
527             mYesNoLayout.setVisibility(View.VISIBLE);
528             mNormalLayout.setVisibility(View.GONE);
529             break;
530         }
531     }
532 
getFontSizeFactor(FontSize size)533     private float getFontSizeFactor(FontSize size) {
534         final float[] fontSizes =
535             {NORMAL_FONT_FACTOR, LARGE_FONT_FACTOR, SMALL_FONT_FACTOR};
536 
537         return fontSizes[size.ordinal()];
538     }
539 
initFromIntent(Intent intent)540     private void initFromIntent(Intent intent) {
541         // Get the calling intent type: text/key, and setup the
542         // display parameters.
543         CatLog.d(LOG_TAG, "initFromIntent - slot id: " + mSlotId);
544         if (intent != null) {
545             mStkInput = intent.getParcelableExtra("INPUT");
546             mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1);
547             CatLog.d(LOG_TAG, "onCreate - slot id: " + mSlotId);
548             if (mStkInput == null) {
549                 finish();
550             } else {
551                 mState = mStkInput.yesNo ? STATE_YES_NO :
552                         STATE_TEXT;
553                 configInputDisplay();
554             }
555         } else {
556             finish();
557         }
558     }
559 }
560