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