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