1 /* 2 * Copyright (C) 2009 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 17 package com.android.phone; 18 19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 20 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.app.Dialog; 24 import android.app.ProgressDialog; 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.DialogInterface; 29 import android.content.DialogInterface.OnCancelListener; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.ServiceConnection; 33 import android.os.AsyncResult; 34 import android.os.Bundle; 35 import android.os.CountDownTimer; 36 import android.os.Handler; 37 import android.os.IBinder; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.telephony.TelephonyManager; 41 import android.util.Log; 42 43 import com.android.internal.telephony.Phone; 44 import com.android.internal.telephony.TelephonyIntents; 45 46 /** 47 * Displays dialog that enables users to exit Emergency Callback Mode 48 * 49 * @see EmergencyCallbackModeService 50 */ 51 public class EmergencyCallbackModeExitDialog extends Activity implements OnCancelListener { 52 53 private static final String TAG = "EmergencyCallbackMode"; 54 55 /** Intent to trigger the Emergency Callback Mode exit dialog */ 56 static final String ACTION_SHOW_ECM_EXIT_DIALOG = 57 "com.android.phone.action.ACTION_SHOW_ECM_EXIT_DIALOG"; 58 59 public static final int EXIT_ECM_BLOCK_OTHERS = 1; 60 public static final int EXIT_ECM_DIALOG = 2; 61 public static final int EXIT_ECM_PROGRESS_DIALOG = 3; 62 public static final int EXIT_ECM_IN_EMERGENCY_CALL_DIALOG = 4; 63 64 AlertDialog mAlertDialog = null; 65 ProgressDialog mProgressDialog = null; 66 CountDownTimer mTimer = null; 67 EmergencyCallbackModeService mService = null; 68 Handler mHandler = null; 69 int mDialogType = 0; 70 long mEcmTimeout = 0; 71 private boolean mInEmergencyCall = false; 72 private static final int ECM_TIMER_RESET = 1; 73 private Phone mPhone = null; 74 private boolean mIsResumed = false; 75 76 @Override onCreate(Bundle savedInstanceState)77 public void onCreate(Bundle savedInstanceState) { 78 super.onCreate(savedInstanceState); 79 getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 80 mPhone = PhoneGlobals.getInstance().getPhoneInEcm(); 81 // Check if phone is in Emergency Callback Mode. If not, exit. 82 if (mPhone == null || !mPhone.isInEcm()) { 83 Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone); 84 finish(); 85 return; 86 } 87 Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone); 88 89 mHandler = new Handler(); 90 91 // Start thread that will wait for the connection completion so that it can get 92 // timeout value from the service 93 Thread waitForConnectionCompleteThread = new Thread(null, mTask, 94 "EcmExitDialogWaitThread"); 95 waitForConnectionCompleteThread.start(); 96 97 // Register ECM timer reset notfication 98 mPhone.registerForEcmTimerReset(mTimerResetHandler, ECM_TIMER_RESET, null); 99 100 // Register receiver for intent closing the dialog 101 IntentFilter filter = new IntentFilter(); 102 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 103 registerReceiver(mEcmExitReceiver, filter); 104 } 105 106 @Override onResume()107 public void onResume() { 108 super.onResume(); 109 mIsResumed = true; 110 } 111 112 @Override onPause()113 public void onPause() { 114 super.onPause(); 115 mIsResumed = false; 116 } 117 118 @Override onDestroy()119 public void onDestroy() { 120 super.onDestroy(); 121 try { 122 unregisterReceiver(mEcmExitReceiver); 123 } catch (IllegalArgumentException e) { 124 // Receiver was never registered - silently ignore. 125 } 126 // Unregister ECM timer reset notification 127 if (mPhone != null) { 128 mPhone.unregisterForEcmTimerReset(mHandler); 129 } 130 } 131 132 @Override onRestoreInstanceState(Bundle savedInstanceState)133 protected void onRestoreInstanceState(Bundle savedInstanceState) { 134 super.onRestoreInstanceState(savedInstanceState); 135 mDialogType = savedInstanceState.getInt("DIALOG_TYPE"); 136 } 137 138 @Override onSaveInstanceState(Bundle outState)139 protected void onSaveInstanceState(Bundle outState) { 140 super.onSaveInstanceState(outState); 141 outState.putInt("DIALOG_TYPE", mDialogType); 142 } 143 144 /** 145 * Waits until bind to the service completes 146 */ 147 private Runnable mTask = new Runnable() { 148 public void run() { 149 Looper.prepare(); 150 151 // Bind to the remote service 152 bindService(new Intent(EmergencyCallbackModeExitDialog.this, 153 EmergencyCallbackModeService.class), mConnection, Context.BIND_AUTO_CREATE); 154 155 // Wait for bind to finish 156 synchronized (EmergencyCallbackModeExitDialog.this) { 157 try { 158 if (mService == null) { 159 EmergencyCallbackModeExitDialog.this.wait(); 160 } 161 } catch (InterruptedException e) { 162 Log.d("ECM", "EmergencyCallbackModeExitDialog InterruptedException: " 163 + e.getMessage()); 164 e.printStackTrace(); 165 } 166 } 167 168 // Get timeout value and call state from the service 169 if (mService != null) { 170 mEcmTimeout = mService.getEmergencyCallbackModeTimeout(); 171 mInEmergencyCall = mService.getEmergencyCallbackModeCallState(); 172 try { 173 // Unbind from remote service 174 unbindService(mConnection); 175 } catch (IllegalArgumentException e) { 176 // Failed to unbind from service. Don't crash as this brings down the entire 177 // radio. 178 Log.w(TAG, "Failed to unbind from EmergencyCallbackModeService"); 179 } 180 } 181 182 // Show dialog 183 mHandler.post(new Runnable() { 184 public void run() { 185 showEmergencyCallbackModeExitDialog(); 186 } 187 }); 188 } 189 }; 190 191 /** 192 * Shows Emergency Callback Mode dialog and starts countdown timer 193 */ showEmergencyCallbackModeExitDialog()194 private void showEmergencyCallbackModeExitDialog() { 195 if (isDestroyed()) { 196 Log.w(TAG, "Tried to show dialog, but activity was already finished"); 197 return; 198 } 199 if(mInEmergencyCall) { 200 mDialogType = EXIT_ECM_IN_EMERGENCY_CALL_DIALOG; 201 showDialog(EXIT_ECM_IN_EMERGENCY_CALL_DIALOG); 202 } else { 203 if (getIntent().getAction().equals( 204 TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) { 205 mDialogType = EXIT_ECM_BLOCK_OTHERS; 206 showDialog(EXIT_ECM_BLOCK_OTHERS); 207 } else if (getIntent().getAction().equals(ACTION_SHOW_ECM_EXIT_DIALOG)) { 208 mDialogType = EXIT_ECM_DIALOG; 209 showDialog(EXIT_ECM_DIALOG); 210 } 211 212 mTimer = new CountDownTimer(mEcmTimeout, 1000) { 213 @Override 214 public void onTick(long millisUntilFinished) { 215 CharSequence text = getDialogText(millisUntilFinished); 216 mAlertDialog.setMessage(text); 217 } 218 219 @Override 220 public void onFinish() { 221 //Do nothing 222 } 223 }.start(); 224 } 225 } 226 227 /** 228 * Creates dialog that enables users to exit Emergency Callback Mode 229 */ 230 @Override onCreateDialog(int id)231 protected Dialog onCreateDialog(int id) { 232 switch (id) { 233 case EXIT_ECM_BLOCK_OTHERS: 234 case EXIT_ECM_DIALOG: 235 CharSequence text = getDialogText(mEcmTimeout); 236 mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this, 237 android.R.style.Theme_DeviceDefault_Dialog_Alert) 238 .setIcon(R.drawable.ic_emergency_callback_mode) 239 .setTitle(R.string.phone_in_ecm_notification_title) 240 .setMessage(text) 241 .setPositiveButton(R.string.alert_dialog_yes, 242 new DialogInterface.OnClickListener() { 243 public void onClick(DialogInterface dialog,int whichButton) { 244 // User clicked Yes. Exit Emergency Callback Mode. 245 mPhone.exitEmergencyCallbackMode(); 246 247 // Show progress dialog 248 showDialog(EXIT_ECM_PROGRESS_DIALOG); 249 mTimer.cancel(); 250 } 251 }) 252 .setNegativeButton(R.string.alert_dialog_no, 253 new DialogInterface.OnClickListener() { 254 public void onClick(DialogInterface dialog, int whichButton) { 255 // User clicked No 256 setResult(RESULT_CANCELED); 257 finish(); 258 } 259 }).create(); 260 mAlertDialog.setOnCancelListener(this); 261 return mAlertDialog; 262 263 case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG: 264 mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this, 265 android.R.style.Theme_DeviceDefault_Dialog_Alert) 266 .setIcon(R.drawable.ic_emergency_callback_mode) 267 .setTitle(R.string.phone_in_ecm_notification_title) 268 .setMessage(R.string.alert_dialog_in_ecm_call) 269 .setNeutralButton(R.string.alert_dialog_dismiss, 270 new DialogInterface.OnClickListener() { 271 public void onClick(DialogInterface dialog, int whichButton) { 272 // User clicked Dismiss 273 setResult(RESULT_CANCELED); 274 finish(); 275 } 276 }).create(); 277 mAlertDialog.setOnCancelListener(this); 278 return mAlertDialog; 279 280 case EXIT_ECM_PROGRESS_DIALOG: 281 mProgressDialog = new ProgressDialog(EmergencyCallbackModeExitDialog.this); 282 mProgressDialog.setMessage(getText(R.string.progress_dialog_exiting_ecm)); 283 mProgressDialog.setIndeterminate(true); 284 mProgressDialog.setCancelable(false); 285 return mProgressDialog; 286 287 default: 288 return null; 289 } 290 } 291 292 /** 293 * Returns dialog box text with updated timeout value 294 */ getDialogText(long millisUntilFinished)295 private CharSequence getDialogText(long millisUntilFinished) { 296 // Format time 297 int minutes = (int)(millisUntilFinished / 60000); 298 String time = String.format("%d:%02d", minutes, 299 (millisUntilFinished % 60000) / 1000); 300 301 switch (mDialogType) { 302 case EXIT_ECM_BLOCK_OTHERS: 303 return String.format(getResources().getQuantityText( 304 R.plurals.alert_dialog_not_avaialble_in_ecm, minutes).toString(), time); 305 case EXIT_ECM_DIALOG: 306 boolean shouldRestrictData = mPhone.getImsPhone() != null 307 && mPhone.getImsPhone().isInImsEcm(); 308 return String.format(getResources().getQuantityText( 309 // During IMS ECM, data restriction hint should be removed. 310 shouldRestrictData 311 ? R.plurals.alert_dialog_exit_ecm_without_data_restriction_hint 312 : R.plurals.alert_dialog_exit_ecm, 313 minutes).toString(), time); 314 } 315 return null; 316 } 317 318 /** 319 * Closes activity when dialog is canceled 320 */ 321 @Override onCancel(DialogInterface dialog)322 public void onCancel(DialogInterface dialog) { 323 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED); 324 finish(); 325 } 326 327 /** 328 * Listens for Emergency Callback Mode state change intents 329 */ 330 private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() { 331 @Override 332 public void onReceive(Context context, Intent intent) { 333 // Received exit Emergency Callback Mode notification close all dialogs 334 if (intent.getAction().equals( 335 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 336 // Cancel if the sticky broadcast extra for whether or not we are in ECM is false. 337 if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) { 338 if (mAlertDialog != null) 339 mAlertDialog.dismiss(); 340 if (mProgressDialog != null) 341 mProgressDialog.dismiss(); 342 EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK); 343 finish(); 344 } 345 } 346 } 347 }; 348 349 /** 350 * Class for interacting with the interface of the service 351 */ 352 private ServiceConnection mConnection = new ServiceConnection() { 353 public void onServiceConnected(ComponentName className, IBinder service) { 354 mService = ((EmergencyCallbackModeService.LocalBinder)service).getService(); 355 // Notify thread that connection is ready 356 synchronized (EmergencyCallbackModeExitDialog.this) { 357 EmergencyCallbackModeExitDialog.this.notify(); 358 } 359 } 360 361 public void onServiceDisconnected(ComponentName className) { 362 mService = null; 363 } 364 }; 365 366 /** 367 * Class for receiving framework timer reset notifications 368 */ 369 private Handler mTimerResetHandler = new Handler () { 370 public void handleMessage(Message msg) { 371 switch (msg.what) { 372 case ECM_TIMER_RESET: 373 if(!((Boolean)((AsyncResult) msg.obj).result).booleanValue()) { 374 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED); 375 finish(); 376 } 377 break; 378 } 379 } 380 }; 381 } 382