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..
onClick(View v)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
onCreate(Bundle icicle)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
onPostCreate(Bundle savedInstanceState)162     protected void onPostCreate(Bundle savedInstanceState) {
163         super.onPostCreate(savedInstanceState);
164 
165         mTextIn.addTextChangedListener(this);
166     }
167 
168     @Override
onResume()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
onPause()182     public void onPause() {
183         super.onPause();
184         CatLog.d(LOG_TAG, "onPause - mIsResponseSent[" + mIsResponseSent + "]");
185     }
186 
187     @Override
onStop()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
onDestroy()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
onKeyDown(int keyCode, KeyEvent event)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 
sendResponse(int resId)233     void sendResponse(int resId) {
234         sendResponse(resId, null, false);
235     }
236 
sendResponse(int resId, String input, boolean help)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
onCreateOptionsMenu(android.view.Menu menu)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
onPrepareOptionsMenu(android.view.Menu menu)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
onOptionsItemSelected(MenuItem item)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
onSaveInstanceState(Bundle outState)306     protected void onSaveInstanceState(Bundle outState) {
307         CatLog.d(LOG_TAG, "onSaveInstanceState: " + mSlotId);
308         outState.putBoolean("ACCEPT_USERS_INPUT", mAcceptUsersInput);
309     }
310 
311     @Override
onRestoreInstanceState(Bundle savedInstanceState)312     protected void onRestoreInstanceState(Bundle savedInstanceState) {
313         CatLog.d(LOG_TAG, "onRestoreInstanceState: " + mSlotId);
314         mAcceptUsersInput = savedInstanceState.getBoolean("ACCEPT_USERS_INPUT");
315     }
316 
beforeTextChanged(CharSequence s, int start, int count, int after)317     public void beforeTextChanged(CharSequence s, int start, int count,
318             int after) {
319     }
320 
onTextChanged(CharSequence s, int start, int before, int count)321     public void onTextChanged(CharSequence s, int start, int before, int count) {
322         // Reset timeout.
323         startTimeOut();
324     }
325 
afterTextChanged(Editable s)326     public void afterTextChanged(Editable s) {
327     }
328 
verfiyTypedText()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 
cancelTimeOut()338     private void cancelTimeOut() {
339         mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
340     }
341 
startTimeOut()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 
configInputDisplay()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 
getFontSizeFactor(FontSize size)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 
initFromIntent(Intent intent)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