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