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