1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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 com.android.internal.telephony.cat.CatLog; 20import com.android.internal.telephony.cat.TextMessage; 21import com.android.internal.telephony.cat.CatLog; 22 23import android.app.Activity; 24import android.app.AlarmManager; 25import android.app.PendingIntent; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.BroadcastReceiver; 29import android.content.Context; 30import android.graphics.drawable.BitmapDrawable; 31import android.os.Bundle; 32import android.os.SystemClock; 33import android.view.KeyEvent; 34import android.view.View; 35import android.view.Window; 36import android.widget.Button; 37import android.widget.TextView; 38 39/** 40 * AlertDialog used for DISPLAY TEXT commands. 41 * 42 */ 43public class StkDialogActivity extends Activity implements View.OnClickListener { 44 // members 45 private static final String className = new Object(){}.getClass().getEnclosingClass().getName(); 46 private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1); 47 TextMessage mTextMsg = null; 48 private int mSlotId = -1; 49 private StkAppService appService = StkAppService.getInstance(); 50 // Determines whether Terminal Response (TR) has been sent 51 private boolean mIsResponseSent = false; 52 private Context mContext; 53 // Utilize AlarmManager for real-time countdown 54 private PendingIntent mTimeoutIntent; 55 private AlarmManager mAlarmManager; 56 private final static String ALARM_TIMEOUT = "com.android.stk.DIALOG_ALARM_TIMEOUT"; 57 58 //keys) for saving the state of the dialog in the icicle 59 private static final String TEXT = "text"; 60 61 // message id for time out 62 private static final int MSG_ID_TIMEOUT = 1; 63 64 // buttons id 65 public static final int OK_BUTTON = R.id.button_ok; 66 public static final int CANCEL_BUTTON = R.id.button_cancel; 67 68 @Override 69 protected void onCreate(Bundle icicle) { 70 super.onCreate(icicle); 71 72 CatLog.d(LOG_TAG, "onCreate, sim id: " + mSlotId); 73 // New Dialog is created - set to no response sent 74 mIsResponseSent = false; 75 76 requestWindowFeature(Window.FEATURE_LEFT_ICON); 77 78 setContentView(R.layout.stk_msg_dialog); 79 80 Button okButton = (Button) findViewById(R.id.button_ok); 81 Button cancelButton = (Button) findViewById(R.id.button_cancel); 82 83 okButton.setOnClickListener(this); 84 cancelButton.setOnClickListener(this); 85 86 mContext = getBaseContext(); 87 IntentFilter intentFilter = new IntentFilter(); 88 intentFilter.addAction(ALARM_TIMEOUT); 89 mContext.registerReceiver(mBroadcastReceiver, intentFilter); 90 mAlarmManager =(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 91 92 } 93 94 public void onClick(View v) { 95 String input = null; 96 switch (v.getId()) { 97 case OK_BUTTON: 98 CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId); 99 cancelTimeOut(); 100 sendResponse(StkAppService.RES_ID_CONFIRM, true); 101 break; 102 case CANCEL_BUTTON: 103 CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId); 104 cancelTimeOut(); 105 sendResponse(StkAppService.RES_ID_CONFIRM, false); 106 break; 107 } 108 finish(); 109 } 110 111 @Override 112 public boolean onKeyDown(int keyCode, KeyEvent event) { 113 switch (keyCode) { 114 case KeyEvent.KEYCODE_BACK: 115 CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK"); 116 cancelTimeOut(); 117 sendResponse(StkAppService.RES_ID_BACKWARD); 118 finish(); 119 break; 120 } 121 return false; 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent + 128 "], sim id: " + mSlotId); 129 130 initFromIntent(getIntent()); 131 if (mTextMsg == null) { 132 finish(); 133 return; 134 } 135 136 Window window = getWindow(); 137 138 TextView mMessageView = (TextView) window 139 .findViewById(R.id.dialog_message); 140 141 setTitle(mTextMsg.title); 142 143 if (!(mTextMsg.iconSelfExplanatory && mTextMsg.icon != null)) { 144 mMessageView.setText(mTextMsg.text); 145 } 146 147 if (mTextMsg.icon == null) { 148 window.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, 149 com.android.internal.R.drawable.stat_notify_sim_toolkit); 150 } else { 151 window.setFeatureDrawable(Window.FEATURE_LEFT_ICON, 152 new BitmapDrawable(mTextMsg.icon)); 153 } 154 155 /* 156 * If the userClear flag is set and dialogduration is set to 0, the display Text 157 * should be displayed to user forever until some high priority event occurs 158 * (incoming call, MMI code execution etc as mentioned under section 159 * ETSI 102.223, 6.4.1) 160 */ 161 if (StkApp.calculateDurationInMilis(mTextMsg.duration) == 0 && 162 !mTextMsg.responseNeeded && mTextMsg.userClear) { 163 CatLog.d(LOG_TAG, "User should clear text..showing message forever"); 164 return; 165 } 166 167 appService.setDisplayTextDlgVisibility(true, mSlotId); 168 169 /* 170 * When another activity takes the foreground, we do not want the Terminal 171 * Response timer to be restarted when our activity resumes. Hence we will 172 * check if there is an existing timer, and resume it. In this way we will 173 * inform the SIM in correct time when there is no response from the User 174 * to a dialog. 175 */ 176 if (mTimeoutIntent != null) { 177 CatLog.d(LOG_TAG, "Pending Alarm! Let it finish counting down..."); 178 } 179 else { 180 CatLog.d(LOG_TAG, "No Pending Alarm! OK to start timer..."); 181 startTimeOut(mTextMsg.userClear); 182 } 183 } 184 185 @Override 186 public void onPause() { 187 super.onPause(); 188 CatLog.d(LOG_TAG, "onPause, sim id: " + mSlotId); 189 appService.setDisplayTextDlgVisibility(false, mSlotId); 190 191 /* 192 * do not cancel the timer here cancelTimeOut(). If any higher/lower 193 * priority events such as incoming call, new sms, screen off intent, 194 * notification alerts, user actions such as 'User moving to another activtiy' 195 * etc.. occur during Display Text ongoing session, 196 * this activity would receive 'onPause()' event resulting in 197 * cancellation of the timer. As a result no terminal response is 198 * sent to the card. 199 */ 200 } 201 202 @Override 203 protected void onStart() { 204 CatLog.d(LOG_TAG, "onStart, sim id: " + mSlotId); 205 super.onStart(); 206 } 207 208 @Override 209 public void onStop() { 210 super.onStop(); 211 CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" + 212 mIsResponseSent + "], sim id: " + mSlotId); 213 if (!mIsResponseSent) { 214 appService.getStkContext(mSlotId).setPendingDialogInstance(this); 215 } else { 216 CatLog.d(LOG_TAG, "finish."); 217 appService.getStkContext(mSlotId).setPendingDialogInstance(null); 218 cancelTimeOut(); 219 finish(); 220 } 221 } 222 223 @Override 224 public void onDestroy() { 225 super.onDestroy(); 226 CatLog.d(LOG_TAG, "onDestroy - mIsResponseSent[" + mIsResponseSent + 227 "], sim id: " + mSlotId); 228 // if dialog activity is finished by stkappservice 229 // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here 230 // , since the dialog cmd is waiting user to process. 231 if (!mIsResponseSent && !appService.isDialogPending(mSlotId)) { 232 sendResponse(StkAppService.RES_ID_CONFIRM, false); 233 } 234 cancelTimeOut(); 235 // Cleanup broadcast receivers to avoid leaks 236 if (mBroadcastReceiver != null) { 237 unregisterReceiver(mBroadcastReceiver); 238 } 239 } 240 241 @Override 242 public void onSaveInstanceState(Bundle outState) { 243 CatLog.d(LOG_TAG, "onSaveInstanceState"); 244 245 super.onSaveInstanceState(outState); 246 247 outState.putParcelable(TEXT, mTextMsg); 248 } 249 250 @Override 251 public void onRestoreInstanceState(Bundle savedInstanceState) { 252 super.onRestoreInstanceState(savedInstanceState); 253 254 mTextMsg = savedInstanceState.getParcelable(TEXT); 255 CatLog.d(LOG_TAG, "onRestoreInstanceState - [" + mTextMsg + "]"); 256 } 257 258 @Override 259 protected void onNewIntent(Intent intent) { 260 CatLog.d(LOG_TAG, "onNewIntent - updating the same Dialog box"); 261 setIntent(intent); 262 } 263 264 private void sendResponse(int resId, boolean confirmed) { 265 if (mSlotId == -1) { 266 CatLog.d(LOG_TAG, "sim id is invalid"); 267 return; 268 } 269 270 if (StkAppService.getInstance() == null) { 271 CatLog.d(LOG_TAG, "Ignore response: id is " + resId); 272 return; 273 } 274 275 CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] confirmed[" + confirmed + "]"); 276 277 if (mTextMsg.responseNeeded) { 278 Bundle args = new Bundle(); 279 args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE); 280 args.putInt(StkAppService.SLOT_ID, mSlotId); 281 args.putInt(StkAppService.RES_ID, resId); 282 args.putBoolean(StkAppService.CONFIRMATION, confirmed); 283 startService(new Intent(this, StkAppService.class).putExtras(args)); 284 mIsResponseSent = true; 285 } 286 } 287 288 private void sendResponse(int resId) { 289 sendResponse(resId, true); 290 } 291 292 private void initFromIntent(Intent intent) { 293 294 if (intent != null) { 295 mTextMsg = intent.getParcelableExtra("TEXT"); 296 mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1); 297 } else { 298 finish(); 299 } 300 301 CatLog.d(LOG_TAG, "initFromIntent - [" + mTextMsg + "], sim id: " + mSlotId); 302 } 303 304 private void cancelTimeOut() { 305 CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId); 306 if (mTimeoutIntent != null) { 307 mAlarmManager.cancel(mTimeoutIntent); 308 mTimeoutIntent = null; 309 } 310 } 311 312 private void startTimeOut(boolean waitForUserToClear) { 313 314 // Reset timeout. 315 cancelTimeOut(); 316 int dialogDuration = StkApp.calculateDurationInMilis(mTextMsg.duration); 317 // If duration is specified, this has priority. If not, set timeout 318 // according to condition given by the card. 319 if (mTextMsg.userClear == true && mTextMsg.responseNeeded == false) { 320 return; 321 } else { 322 // userClear = false. will disappear after a while. 323 if (dialogDuration == 0) { 324 if (waitForUserToClear) { 325 dialogDuration = StkApp.DISP_TEXT_WAIT_FOR_USER_TIMEOUT; 326 } else { 327 dialogDuration = StkApp.DISP_TEXT_CLEAR_AFTER_DELAY_TIMEOUT; 328 } 329 } 330 CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId); 331 Intent mAlarmIntent = new Intent(ALARM_TIMEOUT); 332 mAlarmIntent.putExtra(StkAppService.SLOT_ID, mSlotId); 333 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); 334 335 // Try to use a more stringent timer not affected by system sleep. 336 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 337 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 338 SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent); 339 } 340 else { 341 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 342 SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent); 343 } 344 } 345 } 346 347 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 348 @Override public void onReceive(Context context, Intent intent) { 349 String action = intent.getAction(); 350 int slotID = intent.getIntExtra(StkAppService.SLOT_ID, 0); 351 352 if (action == null || slotID != mSlotId) return; 353 CatLog.d(LOG_TAG, "onReceive, action=" + action + ", sim id: " + slotID); 354 if (action.equals(ALARM_TIMEOUT)) { 355 CatLog.d(LOG_TAG, "ALARM_TIMEOUT rcvd"); 356 mTimeoutIntent = null; 357 sendResponse(StkAppService.RES_ID_TIMEOUT); 358 finish(); 359 } 360 } 361 }; 362} 363