b/2142065 Added Bluetooth API to allow developer to request the user's permission to enable discovery mode.

Change-Id: I6da1598661ad05b4cfd2b7cc81e05d3053df5777
This commit is contained in:
Michael Chan
2009-09-26 06:16:17 -07:00
parent c63742c8fa
commit e1089ad338
6 changed files with 518 additions and 3 deletions

View File

@@ -487,6 +487,25 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".bluetooth.RequestPermissionActivity"
android:label="@string/bluetooth_permission_request"
android:excludeFromRecents="true"
android:permission="android.permission.BLUETOOTH"
android:theme="@*android:style/Theme.Dialog.Alert">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.REQUEST_DISCOVERABLE" />
<action android:name="android.bluetooth.adapter.action.REQUEST_ENABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".bluetooth.RequestPermissionHelperActivity"
android:label="@string/bluetooth_pairing_request"
android:excludeFromRecents="true"
android:permission="android.permission.BLUETOOTH"
android:theme="@*android:style/Theme.Dialog.Alert">
</activity>
<receiver android:name=".bluetooth.BluetoothPairingRequest"> <receiver android:name=".bluetooth.BluetoothPairingRequest">
<intent-filter> <intent-filter>
<action android:name="android.bluetooth.device.action.PAIRING_REQUEST" /> <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TextView
android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium" />
</ScrollView>

View File

@@ -15,6 +15,11 @@
--> -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Strings for Dialog yes button -->
<string name="yes">"Yes"</string>
<!-- Strings for Dialog no button -->
<string name="no">"No"</string>
<!-- Device Info --> <skip /> <!-- Device Info --> <skip />
<!-- Device Info screen. Used for a status item's value when the proper value is not known --> <!-- Device Info screen. Used for a status item's value when the proper value is not known -->
@@ -227,6 +232,21 @@
<!-- Strings for BluetoothDevicePicker --> <!-- Strings for BluetoothDevicePicker -->
<string name="device_picker">Bluetooth device picker</string> <string name="device_picker">Bluetooth device picker</string>
<!-- Strings for dialog title when asking to the user whether to allow an app to enable discovery mode -->
<string name="bluetooth_permission_request">"Bluetooth permission request"</string>
<!-- Strings for asking to the user whether to allow an app to enable bluetooth -->
<string name="bluetooth_ask_enablement">"An application on your phone is requesting permission to turn on Bluetooth. Do you want to do this?"</string>
<!-- Strings for asking to the user whether to allow an app to enable discovery mode -->
<string name="bluetooth_ask_discovery">"An application on your phone is requesting permission to make your phone discoverable by other Bluetooth devices for <xliff:g id="timeout">%1$d</xliff:g> seconds. Do you want to do this?"</string>
<!-- Strings for asking to the user whether to allow an app to enable bluetooth and discovery mode -->
<string name="bluetooth_ask_enablement_and_discovery">"An application on your phone is requesting permission to turn on Bluetooth and to make your phone discoverable by other devices for <xliff:g id="timeout">%1$d</xliff:g> seconds. Do you want to do this?"</string>
<!-- Strings for msg to display to user while bluetooth is turning on -->
<string name="bluetooth_turning_on">"Turning on Bluetooth\u2026"</string>
<!-- Do not translate. Used for diagnostic screens, precise translation is not necessary --> <!-- Do not translate. Used for diagnostic screens, precise translation is not necessary -->
<string name="bluetooth_scan_text">Empty button\u2026</string> <string name="bluetooth_scan_text">Empty button\u2026</string>
<!-- Do not translate. Used for diagnostic screens, precise translation is not necessary --> <!-- Do not translate. Used for diagnostic screens, precise translation is not necessary -->

View File

@@ -39,9 +39,9 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT = private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
"debug.bt.discoverable_time"; "debug.bt.discoverable_time";
private static final int DISCOVERABLE_TIMEOUT = 120; /* package */ static final int DEFAULT_DISCOVERABLE_TIMEOUT = 120;
private static final String SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP = /* package */ static final String SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP =
"discoverable_end_timestamp"; "discoverable_end_timestamp";
private final Context mContext; private final Context mContext;
@@ -135,7 +135,7 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
private int getDiscoverableTimeout() { private int getDiscoverableTimeout() {
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1); int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
if (timeout <= 0) { if (timeout <= 0) {
timeout = DISCOVERABLE_TIMEOUT; timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
} }
return timeout; return timeout;

View File

@@ -0,0 +1,290 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
/**
* RequestPermissionActivity asks the user whether to enable discovery. This is
* usually started by an application wanted to start bluetooth and or discovery
*/
public class RequestPermissionActivity extends AlertActivity implements
DialogInterface.OnClickListener {
// Command line to test this
// adb shell am start -a android.bluetooth.adapter.action.REQUEST_ENABLE
// adb shell am start -a android.bluetooth.adapter.action.REQUEST_DISCOVERABLE
private static final String TAG = "RequestPermissionActivity";
private static final int MAX_DISCOVERABLE_TIMEOUT = 300;
// Result code: Error
public static final int RESULT_ERROR = -2;
// Result code: User rejected the request
public static final int RESULT_USER_DENIED = -1;
// Non-error return code: BT is starting or has started successfully. Used
// by this Activity and RequestPermissionHelperActivity
/* package */ static final int RESULT_BT_STARTING_OR_STARTED = -1000;
private static final int REQUEST_CODE_START_BT = 1;
private LocalBluetoothManager mLocalManager;
private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
/*
* True if bluetooth wasn't enabled and RequestPermissionHelperActivity was
* started to ask the user and start bt.
*
* If/when that activity returns successfully, display please wait msg then
* go away when bt has started and discovery mode has been enabled.
*/
private boolean mNeededToEnableBluetooth;
// True if requesting BT to be turned on
// False if requesting BT to be turned on + discoverable mode
private boolean mEnableOnly = false;
private boolean mUserConfirmed = false;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null)
return;
if (mNeededToEnableBluetooth
&& BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
if (mUserConfirmed) {
proceedAndFinish(false);
}
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (parseIntent()) {
finish();
return;
}
int btState = mLocalManager.getBluetoothState();
switch (btState) {
case BluetoothAdapter.STATE_OFF:
case BluetoothAdapter.STATE_TURNING_OFF:
case BluetoothAdapter.STATE_TURNING_ON:
/*
* Strictly speaking STATE_TURNING_ON belong with STATE_ON;
* however, BT may not be ready when the user clicks yes and we
* would fail to turn on discovery mode. By kicking this to the
* RequestPermissionHelperActivity, this class will handle that
* case via the broadcast receiver.
*/
/*
* Start the helper activity to:
* 1) ask the user about enabling bt AND discovery
* 2) enable BT upon confirmation
*/
registerReceiver(mReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
Intent i = new Intent();
i.setClass(this, RequestPermissionHelperActivity.class);
if (mEnableOnly) {
i.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
} else {
i.setAction(RequestPermissionHelperActivity.
ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE);
i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout);
}
startActivityForResult(i, REQUEST_CODE_START_BT);
mNeededToEnableBluetooth = true;
break;
case BluetoothAdapter.STATE_ON:
if (mEnableOnly) {
// Nothing to do. Already enabled.
proceedAndFinish(false);
return;
} else {
// Ask the user about enabling discovery mode
createDialog();
break;
}
}
}
private void createDialog() {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_permission_request);
View view = getLayoutInflater().inflate(R.layout.bluetooth_discoverable, null);
p.mView = view;
TextView tv = (TextView) view.findViewById(R.id.message);
if (mNeededToEnableBluetooth) {
// RequestPermissionHelperActivity has gotten confirmation from user
// to turn on BT
tv.setText(getString(R.string.bluetooth_turning_on));
} else {
// Ask the user whether to turn on discovery mode or not
tv.setText(getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout));
p.mPositiveButtonText = getString(R.string.yes);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.no);
p.mNegativeButtonListener = this;
}
setupAlert();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE_START_BT) {
Log.e(TAG, "Unexpected onActivityResult " + requestCode + " " + resultCode);
setResult(RESULT_ERROR);
finish();
return;
}
if (resultCode != RESULT_BT_STARTING_OR_STARTED) {
setResult(resultCode);
finish();
}
// Back from RequestPermissionHelperActivity. User confirmed to enable
// BT and discoverable mode.
mUserConfirmed = true;
if (mLocalManager.getBluetoothState() == BluetoothAdapter.STATE_ON) {
proceedAndFinish(false);
} else {
// If BT is not up yet, show "Turning on Bluetooth..."
createDialog();
}
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
proceedAndFinish(true);
break;
case DialogInterface.BUTTON_NEGATIVE:
setResult(RESULT_USER_DENIED);
break;
}
}
private void proceedAndFinish(boolean buttonPressed) {
int returnCode;
if (mEnableOnly) {
// BT enabled. Done
returnCode = 0;
} else if (mLocalManager.getBluetoothAdapter().setScanMode(
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) {
// If already in discoverable mode, this will extend the timeout.
persistDiscoverableEndTimestamp(System.currentTimeMillis() + mTimeout * 1000);
returnCode = mTimeout;
} else {
returnCode = RESULT_ERROR;
}
setResult(returnCode);
if (!buttonPressed) {
finish();
}
}
private boolean parseIntent() {
Intent intent = getIntent();
if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
mEnableOnly = true;
} else if (intent != null
&& intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)) {
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
Log.e(TAG, "Timeout = " + mTimeout);
if (mTimeout > MAX_DISCOVERABLE_TIMEOUT) {
mTimeout = MAX_DISCOVERABLE_TIMEOUT;
} else if (mTimeout <= 0) {
mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
}
} else {
Log.e(TAG, "Error: this activity may be started only with intent "
+ BluetoothAdapter.ACTION_REQUEST_ENABLE + " or "
+ BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
setResult(RESULT_ERROR);
return true;
}
mLocalManager = LocalBluetoothManager.getInstance(this);
if (mLocalManager == null) {
Log.e(TAG, "Error: there's a problem starting bluetooth");
setResult(RESULT_ERROR);
return true;
}
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mNeededToEnableBluetooth) unregisterReceiver(mReceiver);
}
private void persistDiscoverableEndTimestamp(long endTimestamp) {
SharedPreferences.Editor editor = mLocalManager.getSharedPreferences().edit();
editor.putLong(
BluetoothDiscoverableEnabler.SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP,
endTimestamp);
editor.commit();
}
@Override
public void onBackPressed() {
setResult(RequestPermissionActivity.RESULT_USER_DENIED);
super.onBackPressed();
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
/**
* RequestPermissionHelperActivity asks the user whether to enable discovery.
* This is usually started by RequestPermissionActivity.
*/
public class RequestPermissionHelperActivity extends AlertActivity implements
DialogInterface.OnClickListener {
private static final String TAG = "RequestPermissionHelperActivity";
public static final String ACTION_INTERNAL_REQUEST_BT_ON =
"com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON";
public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE =
"com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE";
private LocalBluetoothManager mLocalManager;
private int mTimeout;
// True if requesting BT to be turned on
// False if requesting BT to be turned on + discoverable mode
private boolean mEnableOnly;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (parseIntent()) {
finish();
return;
}
createDialog();
}
void createDialog() {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_permission_request);
View view = getLayoutInflater().inflate(R.layout.bluetooth_discoverable, null);
p.mView = view;
TextView tv = (TextView) view.findViewById(R.id.message);
if (mEnableOnly) {
tv.setText(getString(R.string.bluetooth_ask_enablement));
} else {
tv.setText(getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout));
}
p.mPositiveButtonText = getString(R.string.yes);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.no);
p.mNegativeButtonListener = this;
setupAlert();
}
public void onClick(DialogInterface dialog, int which) {
int returnCode;
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
int btState = 0;
try {
// TODO There's a better way.
int retryCount = 30;
do {
btState = mLocalManager.getBluetoothState();
Thread.sleep(100);
} while (btState == BluetoothAdapter.STATE_TURNING_OFF && --retryCount > 0);
} catch (InterruptedException e) {
// don't care
}
if (btState == BluetoothAdapter.STATE_TURNING_ON
|| btState == BluetoothAdapter.STATE_ON
|| mLocalManager.getBluetoothAdapter().enable()) {
returnCode = RequestPermissionActivity.RESULT_BT_STARTING_OR_STARTED;
} else {
returnCode = RequestPermissionActivity.RESULT_ERROR;
}
break;
case DialogInterface.BUTTON_NEGATIVE:
returnCode = RequestPermissionActivity.RESULT_USER_DENIED;
break;
default:
return;
}
setResult(returnCode);
}
private boolean parseIntent() {
Intent intent = getIntent();
if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON)) {
mEnableOnly = true;
} else if (intent != null
&& intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE)) {
mEnableOnly = false;
// Value used for display purposes. Not range checking.
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
} else {
setResult(RequestPermissionActivity.RESULT_ERROR);
return true;
}
mLocalManager = LocalBluetoothManager.getInstance(this);
if (mLocalManager == null) {
Log.e(TAG, "Error: there's a problem starting bluetooth");
setResult(RequestPermissionActivity.RESULT_ERROR);
return true;
}
return false;
}
@Override
public void onBackPressed() {
setResult(RequestPermissionActivity.RESULT_USER_DENIED);
super.onBackPressed();
}
}