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
17package com.android.phone;
18
19import android.app.Activity;
20import android.app.Dialog;
21import android.app.ProgressDialog;
22import android.app.AlertDialog;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.DialogInterface.OnDismissListener;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.ServiceConnection;
31import android.content.res.Resources;
32import android.os.AsyncResult;
33import android.os.Bundle;
34import android.os.CountDownTimer;
35import android.os.Handler;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.Message;
39import android.os.SystemProperties;
40import android.util.Log;
41
42import com.android.internal.telephony.Phone;
43import com.android.internal.telephony.TelephonyIntents;
44import com.android.internal.telephony.TelephonyProperties;
45
46/**
47 * Displays dialog that enables users to exit Emergency Callback Mode
48 *
49 * @see EmergencyCallbackModeService
50 */
51public 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