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.settings.bluetooth;
18
19import com.android.settings.R;
20
21import android.app.Activity;
22import android.app.AlertDialog;
23import android.bluetooth.BluetoothAdapter;
24import android.bluetooth.BluetoothDevice;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.os.Bundle;
31import android.util.Log;
32
33/**
34 * RequestPermissionActivity asks the user whether to enable discovery. This is
35 * usually started by an application wanted to start bluetooth and or discovery
36 */
37public class RequestPermissionActivity extends Activity implements
38        DialogInterface.OnClickListener {
39    // Command line to test this
40    // adb shell am start -a android.bluetooth.adapter.action.REQUEST_ENABLE
41    // adb shell am start -a android.bluetooth.adapter.action.REQUEST_DISCOVERABLE
42
43    private static final String TAG = "RequestPermissionActivity";
44
45    private static final int MAX_DISCOVERABLE_TIMEOUT = 3600; // 1 hr
46
47    // Non-error return code: BT is starting or has started successfully. Used
48    // by this Activity and RequestPermissionHelperActivity
49    /* package */ static final int RESULT_BT_STARTING_OR_STARTED = -1000;
50
51    private static final int REQUEST_CODE_START_BT = 1;
52
53    private LocalBluetoothAdapter mLocalAdapter;
54
55    private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
56
57    /*
58     * True if bluetooth wasn't enabled and RequestPermissionHelperActivity was
59     * started to ask the user and start bt.
60     *
61     * If/when that activity returns successfully, display please wait msg then
62     * go away when bt has started and discovery mode has been enabled.
63     */
64    private boolean mNeededToEnableBluetooth;
65
66    // True if requesting BT to be turned on
67    // False if requesting BT to be turned on + discoverable mode
68    private boolean mEnableOnly;
69
70    private boolean mUserConfirmed;
71
72    private AlertDialog mDialog;
73
74    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
75
76        @Override
77        public void onReceive(Context context, Intent intent) {
78            if (intent == null) {
79                return;
80            }
81            if (mNeededToEnableBluetooth
82                    && BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
83                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
84                if (state == BluetoothAdapter.STATE_ON) {
85                    if (mUserConfirmed) {
86                        proceedAndFinish();
87                    }
88                }
89            }
90        }
91    };
92
93    @Override
94    protected void onCreate(Bundle savedInstanceState) {
95        super.onCreate(savedInstanceState);
96
97        // Note: initializes mLocalAdapter and returns true on error
98        if (parseIntent()) {
99            finish();
100            return;
101        }
102
103        int btState = mLocalAdapter.getState();
104
105        switch (btState) {
106            case BluetoothAdapter.STATE_OFF:
107            case BluetoothAdapter.STATE_TURNING_OFF:
108            case BluetoothAdapter.STATE_TURNING_ON:
109                /*
110                 * Strictly speaking STATE_TURNING_ON belong with STATE_ON;
111                 * however, BT may not be ready when the user clicks yes and we
112                 * would fail to turn on discovery mode. By kicking this to the
113                 * RequestPermissionHelperActivity, this class will handle that
114                 * case via the broadcast receiver.
115                 */
116
117                /*
118                 * Start the helper activity to:
119                 * 1) ask the user about enabling bt AND discovery
120                 * 2) enable BT upon confirmation
121                 */
122                registerReceiver(mReceiver,
123                        new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
124                Intent intent = new Intent();
125                intent.setClass(this, RequestPermissionHelperActivity.class);
126                if (mEnableOnly) {
127                    intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
128                } else {
129                    intent.setAction(RequestPermissionHelperActivity.
130                            ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE);
131                    intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout);
132                }
133                startActivityForResult(intent, REQUEST_CODE_START_BT);
134                mNeededToEnableBluetooth = true;
135                break;
136            case BluetoothAdapter.STATE_ON:
137                if (mEnableOnly) {
138                    // Nothing to do. Already enabled.
139                    proceedAndFinish();
140                } else {
141                    // Ask the user about enabling discovery mode
142                    createDialog();
143                }
144                break;
145            default:
146                Log.e(TAG, "Unknown adapter state: " + btState);
147        }
148    }
149
150    private void createDialog() {
151        AlertDialog.Builder builder = new AlertDialog.Builder(this);
152
153        if (mNeededToEnableBluetooth) {
154            // RequestPermissionHelperActivity has gotten confirmation from user
155            // to turn on BT
156            builder.setMessage(getString(R.string.bluetooth_turning_on));
157            builder.setCancelable(false);
158        } else {
159            // Ask the user whether to turn on discovery mode or not
160            // For lasting discoverable mode there is a different message
161            if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) {
162                builder.setMessage(
163                        getString(R.string.bluetooth_ask_lasting_discovery));
164            } else {
165                builder.setMessage(
166                        getString(R.string.bluetooth_ask_discovery, mTimeout));
167            }
168            builder.setPositiveButton(getString(R.string.allow), this);
169            builder.setNegativeButton(getString(R.string.deny), this);
170        }
171
172        mDialog = builder.create();
173        mDialog.show();
174
175        if (getResources().getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog) == true) {
176            // dismiss dialog immediately if settings say so
177            onClick(null, DialogInterface.BUTTON_POSITIVE);
178        }
179    }
180
181    @Override
182    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
183        if (requestCode != REQUEST_CODE_START_BT) {
184            Log.e(TAG, "Unexpected onActivityResult " + requestCode + ' ' + resultCode);
185            setResult(RESULT_CANCELED);
186            finish();
187            return;
188        }
189        if (resultCode != RESULT_BT_STARTING_OR_STARTED) {
190            setResult(resultCode);
191            finish();
192            return;
193        }
194
195        // Back from RequestPermissionHelperActivity. User confirmed to enable
196        // BT and discoverable mode.
197        mUserConfirmed = true;
198
199        if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
200            proceedAndFinish();
201        } else {
202            // If BT is not up yet, show "Turning on Bluetooth..."
203            createDialog();
204        }
205    }
206
207    public void onClick(DialogInterface dialog, int which) {
208        switch (which) {
209            case DialogInterface.BUTTON_POSITIVE:
210                proceedAndFinish();
211                break;
212
213            case DialogInterface.BUTTON_NEGATIVE:
214                setResult(RESULT_CANCELED);
215                finish();
216                break;
217        }
218    }
219
220    private void proceedAndFinish() {
221        int returnCode;
222
223        if (mEnableOnly) {
224            // BT enabled. Done
225            returnCode = RESULT_OK;
226        } else if (mLocalAdapter.setScanMode(
227                BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) {
228            // If already in discoverable mode, this will extend the timeout.
229            long endTime = System.currentTimeMillis() + (long) mTimeout * 1000;
230            LocalBluetoothPreferences.persistDiscoverableEndTimestamp(
231                    this, endTime);
232            if (0 < mTimeout) {
233               BluetoothDiscoverableTimeoutReceiver.setDiscoverableAlarm(this, endTime);
234            }
235            returnCode = mTimeout;
236            // Activity.RESULT_FIRST_USER should be 1
237            if (returnCode < RESULT_FIRST_USER) {
238                returnCode = RESULT_FIRST_USER;
239            }
240        } else {
241            returnCode = RESULT_CANCELED;
242        }
243
244        if (mDialog != null) {
245            mDialog.dismiss();
246        }
247
248        setResult(returnCode);
249        finish();
250    }
251
252    /**
253     * Parse the received Intent and initialize mLocalBluetoothAdapter.
254     * @return true if an error occurred; false otherwise
255     */
256    private boolean parseIntent() {
257        Intent intent = getIntent();
258        if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
259            mEnableOnly = true;
260        } else if (intent != null
261                && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)) {
262            mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
263                    BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
264
265            Log.d(TAG, "Setting Bluetooth Discoverable Timeout = " + mTimeout);
266
267            if (mTimeout < 0 || mTimeout > MAX_DISCOVERABLE_TIMEOUT) {
268                mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
269            }
270        } else {
271            Log.e(TAG, "Error: this activity may be started only with intent "
272                    + BluetoothAdapter.ACTION_REQUEST_ENABLE + " or "
273                    + BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
274            setResult(RESULT_CANCELED);
275            return true;
276        }
277
278        LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
279        if (manager == null) {
280            Log.e(TAG, "Error: there's a problem starting Bluetooth");
281            setResult(RESULT_CANCELED);
282            return true;
283        }
284        mLocalAdapter = manager.getBluetoothAdapter();
285
286        return false;
287    }
288
289    @Override
290    protected void onDestroy() {
291        super.onDestroy();
292        if (mNeededToEnableBluetooth) {
293            unregisterReceiver(mReceiver);
294        }
295    }
296
297    @Override
298    public void onBackPressed() {
299        setResult(RESULT_CANCELED);
300        super.onBackPressed();
301    }
302}
303