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