From f4f8d9f34669bc67b8cd93e7dc060dfddf3a7e51 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 19 Nov 2013 01:32:10 -0800 Subject: BLE peripheral mode (4/4): Settings change for advertising preference. Change-Id: I5721f136267fe25e55f764bb4a6c53acd45b318b --- AndroidManifest.xml | 2 + res/layout/bluetooth_advertising.xml | 46 +++++++++ res/values/strings.xml | 29 +++--- res/xml/bluetooth_local_device_profile.xml | 34 +++++++ .../bluetooth/BluetoothAdvertisingEnabler.java | 47 ++++++++++ .../bluetooth/BluetoothAdvertisingFragment.java | 103 +++++++++++++++++++++ .../bluetooth/BluetoothDiscoverableEnabler.java | 32 ++++++- .../bluetooth/BluetoothLocalDevicePreference.java | 46 +++++++++ .../bluetooth/BluetoothNameDialogFragment.java | 3 + .../settings/bluetooth/BluetoothSettings.java | 35 +++---- .../settings/bluetooth/LocalBluetoothAdapter.java | 11 +++ .../settings/bluetooth/LocalBluetoothManager.java | 9 ++ .../bluetooth/LocalBluetoothPreferences.java | 17 ++++ .../bluetooth/LocalDeviceProfilesSettings.java | 100 ++++++++++++++++++++ .../bluetooth/RequestPermissionActivity.java | 86 ++++++++++++++--- .../bluetooth/RequestPermissionHelperActivity.java | 16 +++- src/com/android/settings/bluetooth/Utils.java | 18 ++++ 17 files changed, 581 insertions(+), 53 deletions(-) create mode 100644 res/layout/bluetooth_advertising.xml create mode 100644 res/xml/bluetooth_local_device_profile.xml create mode 100644 src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java create mode 100644 src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java create mode 100644 src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java create mode 100644 src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7c4be12..40a64b1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1271,6 +1271,8 @@ + + diff --git a/res/layout/bluetooth_advertising.xml b/res/layout/bluetooth_advertising.xml new file mode 100644 index 0000000..8095514 --- /dev/null +++ b/res/layout/bluetooth_advertising.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 7a14051..9c274ed 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -226,13 +226,13 @@ Bluetooth - Visible to all nearby Bluetooth devices (%1$s) + Accepting pairing requests (%1$s) - Visible to all nearby Bluetooth devices + Accepting pairing requests - Not visible to other Bluetooth devices + Not accepting pairing requests - Only visible to paired devices + Not accepting pairing requests Visibility timeout Broadcasting + + On + + Off + Disable profile? @@ -325,22 +330,16 @@ An app wants to make your tablet visible to other Bluetooth devices. You can change this later in Bluetooth settings. An app wants to make your phone visible to other Bluetooth devices. You can change this later in Bluetooth settings. + + An app wants to turn on Bluetooth and make your tablet visible to other devices. You can change this later in Bluetooth settings. + An app wants to turn on Bluetooth and make your phone visible to other devices. You can change this later in Bluetooth settings. + %1$s wants to turn on Bluetooth broadcasting to communicate with other devices nearby. You can change this later in Bluetooth settings. - %1$s wants to turn on Bluetooth and Bluetooth broadcasting to communicate with other devices nearby. You can change this later in Bluetooth settings. + %1$s wants to turn on Bluetooth and Bluetooth broadcasting to communicate with otherdevices nearby. You can change this later in Bluetooth settings. When this feature is turned on, your phone can communicate with other devices nearby.\n\nBroadcasting uses low-power Bluetooth signals. - - - An app wants to turn on Bluetooth and make your tablet visible to other devices for %1$d seconds. - - An app wants to turn on Bluetooth and make your phone visible to other devices for %1$d seconds. - - - An app wants to turn on Bluetooth and make your tablet visible to other devices. You can change this later in Bluetooth settings. - An app wants to turn on Bluetooth and make your phone visible to other devices. You can change this later in Bluetooth settings. - "Turning Bluetooth on\u2026" diff --git a/res/xml/bluetooth_local_device_profile.xml b/res/xml/bluetooth_local_device_profile.xml new file mode 100644 index 0000000..95a110e --- /dev/null +++ b/res/xml/bluetooth_local_device_profile.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java b/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java new file mode 100644 index 0000000..b9e4ee2 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 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.content.Context; +import android.preference.PreferenceScreen; + +import com.android.settings.R; + +/** + * BluetoothAdvertisingEnabler helps manager change of bluetooth advertising preferences. + */ +final class BluetoothAdvertisingEnabler { + + private final Context mContext; + private final PreferenceScreen mBluetoothAdvertisingPreference; + + public BluetoothAdvertisingEnabler(Context context, PreferenceScreen bluetoothBroadcast) { + mContext = context; + mBluetoothAdvertisingPreference = bluetoothBroadcast; + } + + public void resume() { + boolean isBroadcastingEnable = LocalBluetoothPreferences.isAdvertisingEnabled(mContext); + handleAdvertisingStateChange(isBroadcastingEnable); + } + + private void handleAdvertisingStateChange(boolean isBroadcastingEnable) { + mBluetoothAdvertisingPreference.setSummary(isBroadcastingEnable ? + R.string.bluetooth_broadcasting_state_on : + R.string.bluetooth_broadcasting_state_off); + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java b/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java new file mode 100644 index 0000000..ea6adb0 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 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.app.ActionBar; +import android.app.Activity; +import android.app.Fragment; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; + +import com.android.settings.R; + +/** + * Fragment to display and let the user change advertising preference. + */ +public class BluetoothAdvertisingFragment extends Fragment + implements CompoundButton.OnCheckedChangeListener { + + private static final String TAG = "BluetoothAdvertisingFragment"; + private View mView; + private Switch mActionBarSwitch; + private Activity mActivity; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mActivity = getActivity(); + mActionBarSwitch = new Switch(mActivity); + if (mActivity instanceof PreferenceActivity) { + final int padding = mActivity.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mActionBarSwitch.setPaddingRelative(0, 0, padding, 0); + mActivity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM); + mActivity.getActionBar().setCustomView(mActionBarSwitch, new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END)); + mActivity.getActionBar().setTitle(R.string.bluetooth_broadcasting); + } + mActionBarSwitch.setChecked( + LocalBluetoothPreferences.isAdvertisingEnabled(mActivity.getApplicationContext())); + + mActionBarSwitch.setOnCheckedChangeListener(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mView = inflater.inflate(R.layout.bluetooth_advertising, container, false); + initView(mView); + return mView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + getActivity().getActionBar().setCustomView(null); + } + + private void initView(View view) { + mActionBarSwitch.setOnCheckedChangeListener(this); + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean desiredState) { + mActionBarSwitch.setChecked(desiredState); + Context context = getActivity(); + LocalBluetoothPreferences.setAdvertisingEnabled(context, desiredState); + if (!desiredState) { + LocalBluetoothAdapter adapter = + LocalBluetoothManager.getInstance(context).getBluetoothAdapter(); + // Stop advertising if advertising is in process. + if (adapter.isAdvertising()) { + Intent intent = new Intent(BluetoothAdapter.ACTION_STOP_ADVERTISING); + getActivity().startActivity(intent); + } + } + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java index d687136..a040bf2 100755 --- a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java @@ -25,13 +25,10 @@ import android.content.SharedPreferences; import android.os.Handler; import android.os.SystemProperties; import android.preference.Preference; -import android.text.format.DateUtils; +import android.util.Log; import com.android.settings.R; -import android.text.format.Time; -import android.util.Log; - /** * BluetoothDiscoverableEnabler is a helper to manage the "Discoverable" * checkbox. It sets/unsets discoverability and keeps track of how much time @@ -63,6 +60,8 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick private final Context mContext; private final Handler mUiHandler; private final Preference mDiscoveryPreference; + // Preference for visibility time out. Not final as it needs to be set through setter. + private Preference mVisibilityTimeoutPreference; private final LocalBluetoothAdapter mLocalAdapter; @@ -102,6 +101,10 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick discoveryPreference.setPersistent(false); } + public void setVisibilityPreference(Preference visibilityPreference) { + mVisibilityTimeoutPreference = visibilityPreference; + } + public void resume() { if (mLocalAdapter == null) { return; @@ -111,6 +114,7 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick mContext.registerReceiver(mReceiver, filter); mDiscoveryPreference.setOnPreferenceClickListener(this); handleModeChanged(mLocalAdapter.getScanMode()); + updateVisibilityTimeoutDisplay(); } public void pause() { @@ -121,6 +125,9 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable); mContext.unregisterReceiver(mReceiver); mDiscoveryPreference.setOnPreferenceClickListener(null); + if (mVisibilityTimeoutPreference != null) { + mVisibilityTimeoutPreference.setOnPreferenceClickListener(null); + } } public boolean onPreferenceClick(Preference preference) { @@ -148,6 +155,23 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); BluetoothDiscoverableTimeoutReceiver.cancelDiscoverableAlarm(mContext); } + updateVisibilityTimeoutDisplay(); + } + + // Update visibility timeout preference. + private void updateVisibilityTimeoutDisplay() { + if (mVisibilityTimeoutPreference == null) { + return; + } + int index = getDiscoverableTimeoutIndex(); + + String visibilitySummary = ""; + CharSequence[] timeoutChoices = + mContext.getResources().getTextArray(R.array.bluetooth_visibility_timeout_entries); + if (index >= 0 && index < timeoutChoices.length) { + visibilitySummary = timeoutChoices[index].toString(); + } + mVisibilityTimeoutPreference.setSummary(visibilitySummary); } private void updateTimerDisplay(int timeout) { diff --git a/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java new file mode 100644 index 0000000..403c3b4 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 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.content.Context; +import android.preference.Preference; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; + +import com.android.settings.R; + +/** + * A preference screen to show information for local device. + */ +public class BluetoothLocalDevicePreference extends Preference { + private static OnClickListener mSettingsListener; + + public BluetoothLocalDevicePreference(Context context, OnClickListener settingsListener) { + super(context); + mSettingsListener = settingsListener; + setWidgetLayoutResource(R.layout.preference_bluetooth); + } + + @Override + protected void onBindView(View view) { + ImageView deviceDetails = (ImageView) view.findViewById( + R.id.deviceDetails); + deviceDetails.setOnClickListener(mSettingsListener); + super.onBindView(view); + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java index b80e42a..0af9c4e 100644 --- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java @@ -26,6 +26,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; +import android.preference.PreferenceActivity; import android.text.Editable; import android.text.InputFilter; import android.text.TextWatcher; @@ -179,6 +180,8 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements mDeviceNameEdited = false; mDeviceNameView.setText(mLocalAdapter.getName()); } + PreferenceActivity activity = (PreferenceActivity)getActivity(); + activity.showBreadCrumbs(mLocalAdapter.getName(), ""); } public void afterTextChanged(Editable s) { diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 529ee79..4cb36c8 100755 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -51,9 +51,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { private static final String TAG = "BluetoothSettings"; private static final int MENU_ID_SCAN = Menu.FIRST; - private static final int MENU_ID_RENAME_DEVICE = Menu.FIRST + 1; - private static final int MENU_ID_VISIBILITY_TIMEOUT = Menu.FIRST + 2; - private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 3; + private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 1; /* Private intent to show the list of received files */ private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES = @@ -178,12 +176,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId) .setEnabled(bluetoothIsEnabled && !isDiscovering) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device) - .setEnabled(bluetoothIsEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - menu.add(Menu.NONE, MENU_ID_VISIBILITY_TIMEOUT, 0, R.string.bluetooth_visibility_timeout) - .setEnabled(bluetoothIsEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); super.onCreateOptionsMenu(menu, inflater); @@ -198,16 +190,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { } return true; - case MENU_ID_RENAME_DEVICE: - new BluetoothNameDialogFragment().show( - getFragmentManager(), "rename device"); - return true; - - case MENU_ID_VISIBILITY_TIMEOUT: - new BluetoothVisibilityTimeoutFragment().show( - getFragmentManager(), "visibility timeout"); - return true; - case MENU_ID_SHOW_RECEIVED: Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES); getActivity().sendBroadcast(intent); @@ -252,7 +234,8 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { // This device if (mMyDevicePreference == null) { - mMyDevicePreference = new Preference(getActivity()); + mMyDevicePreference = new BluetoothLocalDevicePreference( + getActivity(), mLocalDeviceProfilesListener); } mMyDevicePreference.setTitle(mLocalAdapter.getName()); if (getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) { @@ -360,6 +343,18 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { updateContent(mLocalAdapter.getBluetoothState(), false); } + // Listener for local device profile fragment. + private final View.OnClickListener mLocalDeviceProfilesListener = new View.OnClickListener() { + public void onClick(View v) { + if (isRestrictedAndNotPinProtected()) return; + + ((PreferenceActivity) getActivity()).startPreferencePanel( + LocalDeviceProfilesSettings.class.getName(), null, + 0, mLocalAdapter.getName(), null, 0); + } + }; + + private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() { public void onClick(View v) { // User clicked on advanced options icon for a device in the list diff --git a/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java index 013171c..041ecb7 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java @@ -118,6 +118,9 @@ public final class LocalBluetoothAdapter { return mAdapter.isDiscovering(); } + boolean isAdvertising() { + return mAdapter.isAdvertising(); + } boolean isEnabled() { return mAdapter.isEnabled(); } @@ -126,6 +129,14 @@ public final class LocalBluetoothAdapter { mAdapter.setDiscoverableTimeout(timeout); } + boolean startAdvertising() { + return mAdapter.startAdvertising(); + } + + boolean stopAdvertising() { + return mAdapter.stopAdvertisting(); + } + void setName(String name) { mAdapter.setName(name); } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothManager.java b/src/com/android/settings/bluetooth/LocalBluetoothManager.java index ae8dec2..265cabb 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothManager.java @@ -37,6 +37,7 @@ public final class LocalBluetoothManager { private Context mForegroundActivity; private BluetoothDiscoverableEnabler mDiscoverableEnabler; + private BluetoothAdvertisingEnabler mAdvertisingEnabler; private final LocalBluetoothAdapter mLocalAdapter; @@ -70,6 +71,14 @@ public final class LocalBluetoothManager { return mDiscoverableEnabler; } + public void setBluetoothAdvertisingEnabler(BluetoothAdvertisingEnabler advertisingEnabler) { + this.mAdvertisingEnabler = advertisingEnabler; + } + + public BluetoothAdvertisingEnabler getAdvertisingEnabler() { + return mAdvertisingEnabler; + } + private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) { mContext = context; mLocalAdapter = adapter; diff --git a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java index f00b801..f862f72 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java @@ -17,8 +17,10 @@ package com.android.settings.bluetooth; import android.app.QueuedWork; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.util.Log; @@ -46,6 +48,10 @@ final class LocalBluetoothPreferences { private static final String KEY_DISCOVERABLE_END_TIMESTAMP = "discoverable_end_timestamp"; + private static final String KEY_ADVERTISEMENT_PREFERENCE = "bt_advertisement_perference"; + + private static final boolean DEFAULT_ADVERTISING_ENABLED = false; + private LocalBluetoothPreferences() { } @@ -58,6 +64,17 @@ final class LocalBluetoothPreferences { KEY_DISCOVERABLE_END_TIMESTAMP, 0); } + static boolean isAdvertisingEnabled(Context context) { + return getSharedPreferences(context).getBoolean( + KEY_ADVERTISEMENT_PREFERENCE, DEFAULT_ADVERTISING_ENABLED); + } + + static void setAdvertisingEnabled(Context context, boolean advertisingEnabled) { + Editor preferenceEditor = getSharedPreferences(context).edit(); + preferenceEditor.putBoolean(KEY_ADVERTISEMENT_PREFERENCE, advertisingEnabled); + preferenceEditor.apply(); + } + static boolean shouldShowDialogInForeground(Context context, String deviceAddress) { LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context); diff --git a/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java b/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java new file mode 100644 index 0000000..f01bbf5 --- /dev/null +++ b/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 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.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +/** + * This preference fragment presents the user with the profiles of the local devices and allow them + * to be modified. + */ +public final class LocalDeviceProfilesSettings extends SettingsPreferenceFragment { + private static final String TAG = "LocalDeviceProfilesSettings"; + + private static final String KEY_RENAME_DEVICE = "rename_device"; + private static final String KEY_BROADCASTING = "broadcasting"; + private static final String KEY_VISIBILITY_TIMEOUT = "visibility_timeout"; + + private LocalBluetoothManager mManager; + private BluetoothDiscoverableEnabler mDiscoverableEnabler; + private BluetoothAdvertisingEnabler mAdvertisingEnabler; + + private Preference mDeviceNamePref; + private Preference mVisibilityPref; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.bluetooth_local_device_profile); + getPreferenceScreen().setOrderingAsAdded(true); + mDeviceNamePref = findPreference(KEY_RENAME_DEVICE); + mDeviceNamePref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new BluetoothNameDialogFragment().show(getFragmentManager(), "rename device"); + return true; + } + }); + + mVisibilityPref = findPreference(KEY_VISIBILITY_TIMEOUT); + mVisibilityPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new BluetoothVisibilityTimeoutFragment().show( + getFragmentManager(), "visibility timeout"); + return true; + } + }); + + mManager = LocalBluetoothManager.getInstance(getActivity()); + mDiscoverableEnabler = mManager.getDiscoverableEnabler(); + // Set the visibility timeout preference to the enabler so the visibility timeout + // preference can be updated when the timeout changes. + mDiscoverableEnabler.setVisibilityPreference(mVisibilityPref); + + PreferenceActivity mActivity = (PreferenceActivity)getActivity(); + mActivity.showBreadCrumbs(mManager.getBluetoothAdapter().getName(), ""); + PreferenceScreen bluetoothBroadcast = (PreferenceScreen)findPreference(KEY_BROADCASTING); + mAdvertisingEnabler = new BluetoothAdvertisingEnabler(getActivity(), bluetoothBroadcast); + mManager.setBluetoothAdvertisingEnabler(mAdvertisingEnabler); + } + + @Override + public void onResume() { + super.onResume(); + mManager.setForegroundActivity(getActivity()); + mAdvertisingEnabler.resume(); + mDiscoverableEnabler.resume(); + } + + @Override + public void onPause() { + super.onPause(); + mManager.setForegroundActivity(null); + if (mDiscoverableEnabler != null) { + mDiscoverableEnabler.pause(); + } + } + +} diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java index 9f266a5..1b105de 100644 --- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java +++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java @@ -16,8 +16,6 @@ package com.android.settings.bluetooth; -import com.android.settings.R; - import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; @@ -28,17 +26,22 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; +import android.os.UserHandle; import android.util.Log; +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 + * RequestPermissionActivity asks the user whether to enable bluetooth, discovery or advertisement. + * This is usually started by an application wanted to start bluetooth, discovery or advertisement. */ public class RequestPermissionActivity extends Activity 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 + // adb shell am start -a android.bluetooth.adapter.action.START_ADVERTISING + // adb shell am start -a android.bluetooth.adapter.action.STOP_ADVERTISING private static final String TAG = "RequestPermissionActivity"; @@ -50,6 +53,11 @@ public class RequestPermissionActivity extends Activity implements private static final int REQUEST_CODE_START_BT = 1; + private static final int EXTRA_INTENT_NONE = 0; + private static final int EXTRA_INTENT_DISCOVERY = 1; + private static final int EXTRA_INTENT_START_ADVERTISING = 2; + private static final int EXTRA_INTENT_STOP_ADVERTISING = 3; + private LocalBluetoothAdapter mLocalAdapter; private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT; @@ -67,10 +75,14 @@ public class RequestPermissionActivity extends Activity implements // False if requesting BT to be turned on + discoverable mode private boolean mEnableOnly; + private int mExtraIntent = EXTRA_INTENT_NONE; + private boolean mUserConfirmed; private AlertDialog mDialog; + private Context mContext; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -94,6 +106,8 @@ public class RequestPermissionActivity extends Activity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mContext = getApplicationContext(); + // Note: initializes mLocalAdapter and returns true on error if (parseIntent()) { finish(); @@ -125,21 +139,27 @@ public class RequestPermissionActivity extends Activity implements intent.setClass(this, RequestPermissionHelperActivity.class); if (mEnableOnly) { intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON); - } else { + } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY) { intent.setAction(RequestPermissionHelperActivity. ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout); + } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { + intent.setAction(RequestPermissionHelperActivity. + ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE); + } else if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING) { + // Nothing to do. Advertising cannot be in process with bluetooth disabled. } + startActivityForResult(intent, REQUEST_CODE_START_BT); mNeededToEnableBluetooth = true; break; case BluetoothAdapter.STATE_ON: - if (mEnableOnly) { - // Nothing to do. Already enabled. - proceedAndFinish(); - } else { - // Ask the user about enabling discovery mode + if (needAskUserPermission()) { + // Ask the user for permissions of bluetooth operations. createDialog(); + } else { + // No need to ask for permission, just proceed. + proceedAndFinish(); } break; default: @@ -147,6 +167,19 @@ public class RequestPermissionActivity extends Activity implements } } + private boolean needAskUserPermission() { + if (mEnableOnly) { + return false; + } + if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING) { + return false; + } + if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { + return !LocalBluetoothPreferences.isAdvertisingEnabled(mContext); + } + return true; + } + private void createDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); @@ -155,7 +188,7 @@ public class RequestPermissionActivity extends Activity implements // to turn on BT builder.setMessage(getString(R.string.bluetooth_turning_on)); builder.setCancelable(false); - } else { + } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY) { // Ask the user whether to turn on discovery mode or not // For lasting discoverable mode there is a different message if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) { @@ -167,6 +200,11 @@ public class RequestPermissionActivity extends Activity implements } builder.setPositiveButton(getString(R.string.allow), this); builder.setNegativeButton(getString(R.string.deny), this); + } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { + builder.setMessage(getString(R.string.bluetooth_ask_start_broadcast, + Utils.getCallingApp(this))); + builder.setPositiveButton(getString(R.string.allow), this); + builder.setNegativeButton(getString(R.string.deny), this); } mDialog = builder.create(); @@ -223,7 +261,8 @@ public class RequestPermissionActivity extends Activity implements if (mEnableOnly) { // BT enabled. Done returnCode = RESULT_OK; - } else if (mLocalAdapter.setScanMode( + } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY + && mLocalAdapter.setScanMode( BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) { // If already in discoverable mode, this will extend the timeout. long endTime = System.currentTimeMillis() + (long) mTimeout * 1000; @@ -237,6 +276,22 @@ public class RequestPermissionActivity extends Activity implements if (returnCode < RESULT_FIRST_USER) { returnCode = RESULT_FIRST_USER; } + } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { + // Advertise allowed as user said yes. + LocalBluetoothPreferences.setAdvertisingEnabled(mContext, true); + if (mLocalAdapter.startAdvertising()) { + returnCode = RESULT_OK; + Intent intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STARTED); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } else { + returnCode = RESULT_CANCELED; + } + } else if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING + && mLocalAdapter.isAdvertising() + && mLocalAdapter.stopAdvertising()) { + Intent intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + returnCode = RESULT_OK; } else { returnCode = RESULT_CANCELED; } @@ -259,6 +314,7 @@ public class RequestPermissionActivity extends Activity implements mEnableOnly = true; } else if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)) { + mExtraIntent = EXTRA_INTENT_DISCOVERY; mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT); @@ -267,6 +323,12 @@ public class RequestPermissionActivity extends Activity implements if (mTimeout < 0 || mTimeout > MAX_DISCOVERABLE_TIMEOUT) { mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT; } + } else if (intent != null + && intent.getAction().equals(BluetoothAdapter.ACTION_START_ADVERTISING)) { + mExtraIntent = EXTRA_INTENT_START_ADVERTISING; + } else if (intent != null + && intent.getAction().equals(BluetoothAdapter.ACTION_STOP_ADVERTISING)) { + mExtraIntent = EXTRA_INTENT_STOP_ADVERTISING; } else { Log.e(TAG, "Error: this activity may be started only with intent " + BluetoothAdapter.ACTION_REQUEST_ENABLE + " or " diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java index f108513..34874b3 100644 --- a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java +++ b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java @@ -30,7 +30,7 @@ import android.view.View; import android.widget.TextView; /** - * RequestPermissionHelperActivity asks the user whether to enable discovery. + * RequestPermissionHelperActivity asks the user whether to enable discovery or advertisement. * This is usually started by RequestPermissionActivity. */ public class RequestPermissionHelperActivity extends AlertActivity implements @@ -43,6 +43,9 @@ public class RequestPermissionHelperActivity extends AlertActivity implements public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE = "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE"; + public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE = + "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_ADVERTISE"; + private LocalBluetoothAdapter mLocalAdapter; private int mTimeout; @@ -50,6 +53,7 @@ public class RequestPermissionHelperActivity extends AlertActivity implements // True if requesting BT to be turned on // False if requesting BT to be turned on + discoverable mode private boolean mEnableOnly; + private boolean mDiscovery; @Override protected void onCreate(Bundle savedInstanceState) { @@ -75,12 +79,15 @@ public class RequestPermissionHelperActivity extends AlertActivity implements if (mEnableOnly) { p.mMessage = getString(R.string.bluetooth_ask_enablement); - } else { + } else if (mDiscovery) { if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) { p.mMessage = getString(R.string.bluetooth_ask_enablement_and_lasting_discovery); } else { p.mMessage = getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout); } + } else { + p.mMessage = getString(R.string.bluetooth_ask_enablement_and_start_broadcast, + Utils.getCallingApp(this)); } p.mPositiveButtonText = getString(R.string.allow); @@ -138,9 +145,14 @@ public class RequestPermissionHelperActivity extends AlertActivity implements } else if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE)) { mEnableOnly = false; + mDiscovery = true; // Value used for display purposes. Not range checking. mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT); + } else if (intent != null + && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE)) { + mEnableOnly = false; + mDiscovery = false; } else { setResult(RESULT_CANCELED); return true; diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java index fb44d5a..6590b06 100755 --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -16,11 +16,15 @@ package com.android.settings.bluetooth; +import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.widget.Toast; import com.android.settings.R; @@ -102,4 +106,18 @@ final class Utils { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } + + /** + * Get application name of the calling activity. Returns empty string on errors. + */ + static String getCallingApp(Activity activity) { + final PackageManager pm = activity.getApplicationContext().getPackageManager(); + ApplicationInfo applicationInfo; + try { + applicationInfo = pm.getApplicationInfo(activity.getCallingPackage(), 0); + } catch (final NameNotFoundException e) { + applicationInfo = null; + } + return (applicationInfo == null) ? "" : pm.getApplicationLabel(applicationInfo).toString(); + } } -- cgit v1.1