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 com.android.settings.R; 20 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothDevice; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.os.Bundle; 31 import 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 */ 37 public 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