diff options
Diffstat (limited to 'src/com/android/settings/bluetooth')
10 files changed, 758 insertions, 139 deletions
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java b/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java new file mode 100644 index 0000000..83371cd --- /dev/null +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 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.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +public class AdvancedBluetoothSettings extends SettingsPreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private static final String KEY_BT_DISCOVERABLE = "bt_discoverable"; + private static final String KEY_BT_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout"; + private static final String KEY_BT_NAME = "bt_name"; + private static final String KEY_BT_SHOW_RECEIVED = "bt_show_received_files"; + + /* Private intent to show the list of received files */ + private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES = + "android.btopp.intent.action.OPEN_RECEIVED_FILES"; + + private BluetoothDiscoverableEnabler mDiscoverableEnabler; + private BluetoothNamePreference mNamePreference; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.bluetooth_advanced_settings); + + LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(getActivity()); + if (localManager != null) { + LocalBluetoothAdapter localAdapter = localManager.getBluetoothAdapter(); + mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(), + localAdapter, + (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE), + (ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT)); + } + + mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + mDiscoverableEnabler.resume(); + mNamePreference.resume(); + } + + @Override + public void onPause() { + super.onPause(); + + mNamePreference.pause(); + mDiscoverableEnabler.pause(); + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + if (KEY_BT_SHOW_RECEIVED.equals(preference.getKey())) { + Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES); + getActivity().sendBroadcast(intent); + return true; + } + + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + return true; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java index 00e342c..e4f11a2 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java @@ -42,6 +42,9 @@ final class BluetoothDeviceFilter { /** Bonded devices only filter (referenced directly). */ static final Filter BONDED_DEVICE_FILTER = new BondedDeviceFilter(); + /** Unbonded devices only filter (referenced directly). */ + static final Filter UNBONDED_DEVICE_FILTER = new UnbondedDeviceFilter(); + /** Table of singleton filter objects. */ private static final Filter[] FILTERS = { ALL_FILTER, // FILTER_TYPE_ALL @@ -85,6 +88,13 @@ final class BluetoothDeviceFilter { } } + /** Filter that matches only unbonded devices. */ + private static final class UnbondedDeviceFilter implements Filter { + public boolean matches(BluetoothDevice device) { + return device.getBondState() != BluetoothDevice.BOND_BONDED; + } + } + /** Parent class of filters based on UUID and/or Bluetooth class. */ private abstract static class ClassUuidFilter implements Filter { abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass); diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 391c941..06c708b 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -49,8 +49,6 @@ public final class BluetoothDevicePreference extends Preference implements private final CachedBluetoothDevice mCachedDevice; - private ImageView mDeviceSettings; - private OnClickListener mOnSettingsClickListener; private AlertDialog mDisconnectDialog; @@ -121,13 +119,13 @@ public final class BluetoothDevicePreference extends Preference implements btClass.setImageResource(getBtClassDrawable()); btClass.setAlpha(isEnabled() ? 255 : sDimAlpha); btClass.setVisibility(View.VISIBLE); - mDeviceSettings = (ImageView) view.findViewById(R.id.deviceDetails); + ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails); if (mOnSettingsClickListener != null) { - mDeviceSettings.setOnClickListener(this); - mDeviceSettings.setTag(mCachedDevice); - mDeviceSettings.setAlpha(isEnabled() ? 255 : sDimAlpha); + deviceDetails.setOnClickListener(this); + deviceDetails.setTag(mCachedDevice); + deviceDetails.setAlpha(isEnabled() ? 255 : sDimAlpha); } else { // Hide the settings icon and divider - mDeviceSettings.setVisibility(View.GONE); + deviceDetails.setVisibility(View.GONE); View divider = view.findViewById(R.id.divider); if (divider != null) { divider.setVisibility(View.GONE); @@ -152,13 +150,13 @@ public final class BluetoothDevicePreference extends Preference implements } public void onClick(View v) { - if (v == mDeviceSettings) { - if (mOnSettingsClickListener != null) { - mOnSettingsClickListener.onClick(v); - } + // Should never be null by construction + if (mOnSettingsClickListener != null) { + mOnSettingsClickListener.onClick(v); } } + @Override public boolean equals(Object o) { if ((o == null) || !(o instanceof BluetoothDevicePreference)) { return false; @@ -167,6 +165,7 @@ public final class BluetoothDevicePreference extends Preference implements ((BluetoothDevicePreference) o).mCachedDevice); } + @Override public int hashCode() { return mCachedDevice.hashCode(); } @@ -174,8 +173,8 @@ public final class BluetoothDevicePreference extends Preference implements @Override public int compareTo(Preference another) { if (!(another instanceof BluetoothDevicePreference)) { - // Put other preference types above us - return 1; + // Rely on default sort + return super.compareTo(another); } return mCachedDevice diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index 79f23bb..f08e083 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -16,28 +16,27 @@ package com.android.settings.bluetooth; -import com.android.settings.R; -import com.android.settings.WirelessSettings; - import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.preference.Preference; -import android.preference.CheckBoxPreference; import android.provider.Settings; +import android.widget.CompoundButton; +import android.widget.Switch; import android.widget.Toast; +import com.android.settings.R; +import com.android.settings.WirelessSettings; + /** * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox * preference. It turns on/off Bluetooth and ensures the summary of the * preference reflects the current state. */ -public final class BluetoothEnabler implements Preference.OnPreferenceChangeListener { +public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeListener { private final Context mContext; - private final CheckBoxPreference mCheckBox; - private final CharSequence mOriginalSummary; + private Switch mSwitch; private final LocalBluetoothAdapter mLocalAdapter; private final IntentFilter mIntentFilter; @@ -50,17 +49,15 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList } }; - public BluetoothEnabler(Context context, CheckBoxPreference checkBox) { + public BluetoothEnabler(Context context, Switch switch_) { mContext = context; - mCheckBox = checkBox; - mOriginalSummary = checkBox.getSummary(); - checkBox.setPersistent(false); + mSwitch = switch_; LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context); if (manager == null) { // Bluetooth is not supported mLocalAdapter = null; - checkBox.setEnabled(false); + mSwitch.setEnabled(false); } else { mLocalAdapter = manager.getBluetoothAdapter(); } @@ -69,6 +66,7 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList public void resume() { if (mLocalAdapter == null) { + mSwitch.setEnabled(false); return; } @@ -76,7 +74,7 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList handleStateChanged(mLocalAdapter.getBluetoothState()); mContext.registerReceiver(mReceiver, mIntentFilter); - mCheckBox.setOnPreferenceChangeListener(this); + mSwitch.setOnCheckedChangeListener(this); } public void pause() { @@ -85,51 +83,57 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList } mContext.unregisterReceiver(mReceiver); - mCheckBox.setOnPreferenceChangeListener(null); + mSwitch.setOnCheckedChangeListener(null); } - public boolean onPreferenceChange(Preference preference, Object value) { - boolean enable = (Boolean) value; + public void setSwitch(Switch switch_) { + if (mSwitch == switch_) return; + mSwitch.setOnCheckedChangeListener(null); + mSwitch = switch_; + mSwitch.setOnCheckedChangeListener(this); + + int bluetoothState = BluetoothAdapter.STATE_OFF; + if (mLocalAdapter != null) bluetoothState = mLocalAdapter.getBluetoothState(); + boolean isOn = bluetoothState == BluetoothAdapter.STATE_ON; + boolean isOff = bluetoothState == BluetoothAdapter.STATE_OFF; + mSwitch.setChecked(isOn); + mSwitch.setEnabled(isOn || isOff); + } + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // Show toast message if Bluetooth is not allowed in airplane mode - if (enable && !WirelessSettings - .isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) { - Toast.makeText(mContext, R.string.wifi_in_airplane_mode, - Toast.LENGTH_SHORT).show(); - return false; + if (isChecked && + !WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) { + Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show(); + // Reset switch to off + buttonView.setChecked(false); } - mLocalAdapter.setBluetoothEnabled(enable); - mCheckBox.setEnabled(false); - - // Don't update UI to opposite state until we're sure - return false; + if (mLocalAdapter != null) { + mLocalAdapter.setBluetoothEnabled(isChecked); + } + mSwitch.setEnabled(false); } void handleStateChanged(int state) { switch (state) { case BluetoothAdapter.STATE_TURNING_ON: - mCheckBox.setSummary(R.string.wifi_starting); - mCheckBox.setEnabled(false); + mSwitch.setEnabled(false); break; case BluetoothAdapter.STATE_ON: - mCheckBox.setChecked(true); - mCheckBox.setSummary(null); - mCheckBox.setEnabled(true); + mSwitch.setChecked(true); + mSwitch.setEnabled(true); break; case BluetoothAdapter.STATE_TURNING_OFF: - mCheckBox.setSummary(R.string.wifi_stopping); - mCheckBox.setEnabled(false); + mSwitch.setEnabled(false); break; case BluetoothAdapter.STATE_OFF: - mCheckBox.setChecked(false); - mCheckBox.setSummary(mOriginalSummary); - mCheckBox.setEnabled(true); + mSwitch.setChecked(false); + mSwitch.setEnabled(true); break; default: - mCheckBox.setChecked(false); - mCheckBox.setSummary(R.string.wifi_error); - mCheckBox.setEnabled(true); + mSwitch.setChecked(false); + mSwitch.setEnabled(true); } } } diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java new file mode 100644 index 0000000..1a0965c --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011 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.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.preference.Preference; +import android.util.Log; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Button; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +import com.android.settings.R; + +/** + * BluetoothPermissionActivity shows a dialog for accepting incoming + * profile connection request from untrusted devices. + * It is also used to show a dialogue for accepting incoming phonebook + * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR. + */ +public class BluetoothPermissionActivity extends AlertActivity implements + DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener { + private static final String TAG = "BluetoothPermissionActivity"; + private static final boolean DEBUG = Utils.D; + + private View mView; + private TextView messageView; + private Button mOkButton; + private BluetoothDevice mDevice; + + private CheckBox mAlwaysAllowed; + private boolean mAlwaysAllowedValue = true; + + private String mReturnPackage = null; + private String mReturnClass = null; + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (mDevice.equals(device)) dismissDialog(); + } + } + }; + + private void dismissDialog() { + this.dismiss(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent i = getIntent(); + String action = i.getAction(); + mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + mReturnPackage = i.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME); + mReturnClass = i.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME); + + if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { + mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, + BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) == + BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) { + showConnectionDialog(); + } else { + showPbapDialog(); + } + } else { + Log.e(TAG, "Error: this activity may be started only with intent " + + "ACTION_CONNECTION_ACCESS_REQUEST"); + finish(); + } + registerReceiver(mReceiver, + new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)); + } + + private void showConnectionDialog() { + final AlertController.AlertParams p = mAlertParams; + p.mIconId = android.R.drawable.ic_dialog_info; + p.mTitle = getString(R.string.bluetooth_connection_permission_request); + p.mView = createConnectionDialogView(); + p.mPositiveButtonText = getString(R.string.yes); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(R.string.no); + p.mNegativeButtonListener = this; + mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); + setupAlert(); + } + + private void showPbapDialog() { + final AlertController.AlertParams p = mAlertParams; + p.mIconId = android.R.drawable.ic_dialog_info; + p.mTitle = getString(R.string.bluetooth_phonebook_request); + p.mView = createPbapDialogView(); + p.mPositiveButtonText = getString(android.R.string.yes); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(android.R.string.no); + p.mNegativeButtonListener = this; + mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); + setupAlert(); + } + + private String createConnectionDisplayText() { + String mRemoteName = mDevice != null ? mDevice.getName() : null; + + if (mRemoteName == null) mRemoteName = getString(R.string.unknown); + String mMessage1 = getString(R.string.bluetooth_connection_dialog_text, + mRemoteName); + return mMessage1; + } + + private String createPbapDisplayText() { + String mRemoteName = mDevice != null ? mDevice.getName() : null; + + if (mRemoteName == null) mRemoteName = getString(R.string.unknown); + String mMessage1 = getString(R.string.bluetooth_pb_acceptance_dialog_text, + mRemoteName, mRemoteName); + return mMessage1; + } + + private View createConnectionDialogView() { + mView = getLayoutInflater().inflate(R.layout.bluetooth_connection_access, null); + messageView = (TextView)mView.findViewById(R.id.message); + messageView.setText(createConnectionDisplayText()); + return mView; + } + + private View createPbapDialogView() { + mView = getLayoutInflater().inflate(R.layout.bluetooth_pb_access, null); + messageView = (TextView)mView.findViewById(R.id.message); + messageView.setText(createPbapDisplayText()); + mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.bluetooth_pb_alwaysallowed); + mAlwaysAllowed.setChecked(true); + mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + mAlwaysAllowedValue = true; + } else { + mAlwaysAllowedValue = false; + } + } + }); + return mView; + } + + private void onPositive() { + if (DEBUG) Log.d(TAG, "onPositive mAlwaysAllowedValue: " + mAlwaysAllowedValue); + sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, true, + BluetoothDevice.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue); + finish(); + } + + private void onNegative() { + if (DEBUG) Log.d(TAG, "onNegative mAlwaysAllowedValue: " + mAlwaysAllowedValue); + sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, false, + null, false // dummy value, no effect since last param is null + ); + finish(); + } + + private void sendIntentToReceiver(final String intentName, final boolean allowed, + final String extraName, final boolean extraValue) { + Intent intent = new Intent(intentName); + + if (mReturnPackage != null && mReturnClass != null) { + intent.setClassName(mReturnPackage, mReturnClass); + } + + intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + allowed ? BluetoothDevice.CONNECTION_ACCESS_YES : + BluetoothDevice.CONNECTION_ACCESS_NO); + + if (extraName != null) { + intent.putExtra(extraName, extraValue); + } + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN); + } + + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + onPositive(); + break; + + case DialogInterface.BUTTON_NEGATIVE: + onNegative(); + break; + default: + break; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } + + public boolean onPreferenceChange(Preference preference, Object newValue) { + return true; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java new file mode 100644 index 0000000..c769ba6 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 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.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.PowerManager; +import android.util.Log; + +import com.android.settings.R; + +/** + * BluetoothPermissionRequest is a receiver to receive Bluetooth connection + * access request. + */ +public final class BluetoothPermissionRequest extends BroadcastReceiver { + + private static final String TAG = "BluetoothPermissionRequest"; + private static final boolean DEBUG = Utils.V; + public static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (DEBUG) Log.d(TAG, "onReceive"); + + if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { + // convert broadcast intent into activity intent (same action string) + BluetoothDevice device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, + BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION); + String returnPackage = intent.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME); + String returnClass = intent.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME); + + Intent connectionAccessIntent = new Intent(action); + connectionAccessIntent.setClass(context, BluetoothPermissionActivity.class); + connectionAccessIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, requestType); + connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, returnPackage); + connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, returnClass); + + String deviceAddress = device != null ? device.getAddress() : null; + + PowerManager powerManager = + (PowerManager) context.getSystemService(Context.POWER_SERVICE); + + if (powerManager.isScreenOn() && + LocalBluetoothPreferences.shouldShowDialogInForeground(context, deviceAddress) ) { + context.startActivity(connectionAccessIntent); + } else { + // Put up a notification that leads to the dialog + + // Create an intent triggered by clicking on the + // "Clear All Notifications" button + + Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); + deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + BluetoothDevice.CONNECTION_ACCESS_NO); + + Notification notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, + context.getString(R.string.bluetooth_connection_permission_request), + System.currentTimeMillis()); + String deviceName = device != null ? device.getName() : null; + notification.setLatestEventInfo(context, + context.getString(R.string.bluetooth_connection_permission_request), + context.getString(R.string.bluetooth_connection_notif_message, deviceName), + PendingIntent.getActivity(context, 0, connectionAccessIntent, 0)); + notification.flags = Notification.FLAG_AUTO_CANCEL | + Notification.FLAG_ONLY_ALERT_ONCE; + notification.defaults = Notification.DEFAULT_SOUND; + notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); + + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(NOTIFICATION_ID, notification); + } + } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) { + // Remove the notification + NotificationManager manager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + manager.cancel(NOTIFICATION_ID); + } + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 5e4e130..2208223 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -16,16 +16,28 @@ package com.android.settings.bluetooth; +import android.app.ActionBar; +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.content.Intent; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; +import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; +import android.widget.TextView; +import com.android.settings.ProgressCategory; import com.android.settings.R; /** @@ -35,107 +47,253 @@ import com.android.settings.R; public final class BluetoothSettings extends DeviceListPreferenceFragment { private static final String TAG = "BluetoothSettings"; - private static final String KEY_BT_CHECKBOX = "bt_checkbox"; - private static final String KEY_BT_DISCOVERABLE = "bt_discoverable"; - private static final String KEY_BT_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout"; - private static final String KEY_BT_NAME = "bt_name"; - private static final String KEY_BT_SHOW_RECEIVED = "bt_show_received_files"; + private static final int MENU_ID_SCAN = Menu.FIRST; + private static final int MENU_ID_ADVANCED = Menu.FIRST + 1; - private BluetoothEnabler mEnabler; - private BluetoothDiscoverableEnabler mDiscoverableEnabler; - private BluetoothNamePreference mNamePreference; + private BluetoothEnabler mBluetoothEnabler; - /* Private intent to show the list of received files */ - private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES = - "android.btopp.intent.action.OPEN_RECEIVED_FILES"; + private PreferenceGroup mAvailableDevicesCategory; + private boolean mAvailableDevicesCategoryIsPresent; - /** Initialize the filter to show bonded devices only. */ - public BluetoothSettings() { - super(BluetoothDeviceFilter.BONDED_DEVICE_FILTER); + private View mView; + private TextView mEmptyView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mView = inflater.inflate(R.layout.custom_preference_list_fragment, container, false); + return mView; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mEmptyView = (TextView) mView.findViewById(R.id.empty); + getListView().setEmptyView(mEmptyView); } @Override void addPreferencesForActivity() { addPreferencesFromResource(R.xml.bluetooth_settings); - mEnabler = new BluetoothEnabler(getActivity(), - (CheckBoxPreference) findPreference(KEY_BT_CHECKBOX)); + Activity activity = getActivity(); + + Switch actionBarSwitch = new Switch(activity); - mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(), - mLocalAdapter, - (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE), - (ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT)); + if (activity instanceof PreferenceActivity) { + PreferenceActivity preferenceActivity = (PreferenceActivity) activity; + if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { + final int padding = activity.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + actionBarSwitch.setPadding(0, 0, padding, 0); + activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.RIGHT)); + } + } - mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME); + mBluetoothEnabler = new BluetoothEnabler(activity, actionBarSwitch); + + setHasOptionsMenu(true); } @Override public void onResume() { super.onResume(); - // Repopulate (which isn't too bad since it's cached in the settings - // bluetooth manager) - addDevices(); + mBluetoothEnabler.resume(); - mEnabler.resume(); - mDiscoverableEnabler.resume(); - mNamePreference.resume(); + updateContent(mLocalAdapter.getBluetoothState()); } @Override public void onPause() { super.onPause(); - mNamePreference.pause(); - mDiscoverableEnabler.pause(); - mEnabler.pause(); + mBluetoothEnabler.pause(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON; + boolean isDiscovering = mLocalAdapter.isDiscovering(); + int textId = isDiscovering ? R.string.bluetooth_searching_for_devices : + R.string.bluetooth_search_for_devices; + menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId) + //.setIcon(R.drawable.ic_menu_scan_network) + .setEnabled(bluetoothIsEnabled && !isDiscovering) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.bluetooth_menu_advanced) + //.setIcon(android.R.drawable.ic_menu_manage) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ID_SCAN: + if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) { + startScanning(); + } + return true; + case MENU_ID_ADVANCED: + if (getActivity() instanceof PreferenceActivity) { + ((PreferenceActivity) getActivity()).startPreferencePanel( + AdvancedBluetoothSettings.class.getCanonicalName(), + null, + R.string.bluetooth_advanced_titlebar, null, + this, 0); + } else { + startFragment(this, AdvancedBluetoothSettings.class.getCanonicalName(), -1, null); + } + return true; + } + return super.onOptionsItemSelected(item); + } + + private void startScanning() { + if (!mAvailableDevicesCategoryIsPresent) { + getPreferenceScreen().addPreference(mAvailableDevicesCategory); + } + mLocalAdapter.startScanning(true); + } + + @Override + void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { + mLocalAdapter.stopScanning(); + super.onDevicePreferenceClick(btPreference); + } + + private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, + BluetoothDeviceFilter.Filter filter) { + preferenceGroup.setTitle(titleId); + getPreferenceScreen().addPreference(preferenceGroup); + setFilter(filter); + setDeviceListGroup(preferenceGroup); + addCachedDevices(); + preferenceGroup.setEnabled(true); + } + + private void updateContent(int bluetoothState) { + final PreferenceScreen preferenceScreen = getPreferenceScreen(); + getActivity().invalidateOptionsMenu(); + int messageId = 0; + + switch (bluetoothState) { + case BluetoothAdapter.STATE_ON: + preferenceScreen.removeAll(); + preferenceScreen.setOrderingAsAdded(true); + + // This device + if (mMyDevicePreference == null) { + mMyDevicePreference = new Preference(getActivity()); + } + if (mLocalAdapter != null) { + mMyDevicePreference.setTitle(mLocalAdapter.getName()); + } + mMyDevicePreference.setEnabled(true); + preferenceScreen.addPreference(mMyDevicePreference); + + // Paired devices category + if (mPairedDevicesCategory == null) { + mPairedDevicesCategory = new PreferenceCategory(getActivity()); + } else { + mPairedDevicesCategory.removeAll(); + } + addDeviceCategory(mPairedDevicesCategory, + R.string.bluetooth_preference_paired_devices, + BluetoothDeviceFilter.BONDED_DEVICE_FILTER); + int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount(); + + // Available devices category + if (mAvailableDevicesCategory == null) { + mAvailableDevicesCategory = new ProgressCategory(getActivity(), null); + } else { + mAvailableDevicesCategory.removeAll(); + } + addDeviceCategory(mAvailableDevicesCategory, + R.string.bluetooth_preference_found_devices, + BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER); + int numberOfAvailableDevices = mAvailableDevicesCategory.getPreferenceCount(); + mAvailableDevicesCategoryIsPresent = true; + + if (numberOfAvailableDevices == 0) { + preferenceScreen.removePreference(mAvailableDevicesCategory); + mAvailableDevicesCategoryIsPresent = false; + } + + if (numberOfPairedDevices == 0) { + preferenceScreen.removePreference(mPairedDevicesCategory); + startScanning(); + } + return; // not break + + case BluetoothAdapter.STATE_TURNING_OFF: + int preferenceCount = preferenceScreen.getPreferenceCount(); + for (int i = 0; i < preferenceCount; i++) { + preferenceScreen.getPreference(i).setEnabled(false); + } + return; // not break + + case BluetoothAdapter.STATE_OFF: + messageId = R.string.bluetooth_empty_list_bluetooth_off; + break; + + case BluetoothAdapter.STATE_TURNING_ON: + messageId = R.string.bluetooth_turning_on; + break; + } + + setDeviceListGroup(preferenceScreen); + removeAllDevices(); + mEmptyView.setText(messageId); + } + + @Override + public void onBluetoothStateChanged(int bluetoothState) { + super.onBluetoothStateChanged(bluetoothState); + updateContent(bluetoothState); } - private final View.OnClickListener mListener = new View.OnClickListener() { + @Override + public void onScanningStateChanged(boolean started) { + super.onScanningStateChanged(started); + // Update options' enabled state + getActivity().invalidateOptionsMenu(); + } + + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + setDeviceListGroup(getPreferenceScreen()); + removeAllDevices(); + updateContent(mLocalAdapter.getBluetoothState()); + } + + 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 if (v.getTag() instanceof CachedBluetoothDevice) { - CachedBluetoothDevice - device = (CachedBluetoothDevice) v.getTag(); + CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag(); Preference pref = new Preference(getActivity()); pref.setTitle(device.getName()); pref.setFragment(DeviceProfilesSettings.class.getName()); pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, device.getDevice()); - ((PreferenceActivity) getActivity()) - .onPreferenceStartFragment(BluetoothSettings.this, - pref); + ((PreferenceActivity) getActivity()).onPreferenceStartFragment( + BluetoothSettings.this, pref); } else { - Log.w(TAG, "onClick() called for other View: " + v); + Log.w(TAG, "onClick() called for other View: " + v); // TODO remove } } }; - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - if (KEY_BT_SHOW_RECEIVED.equals(preference.getKey())) { - Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES); - getActivity().sendBroadcast(intent); - return true; - } + private Preference mMyDevicePreference; - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - - public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, - int bondState) { - if (bondState == BluetoothDevice.BOND_BONDED) { - // add to "Paired devices" list after remote-initiated pairing - if (mDevicePreferenceMap.get(cachedDevice) == null) { - createDevicePreference(cachedDevice); - } - } else if (bondState == BluetoothDevice.BOND_NONE) { - // remove unpaired device from paired devices list - onDeviceDeleted(cachedDevice); - } - } + private PreferenceGroup mPairedDevicesCategory; /** * Add a listener, which enables the advanced settings icon. @@ -143,6 +301,10 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { */ @Override void initDevicePreference(BluetoothDevicePreference preference) { - preference.setOnSettingsClickListener(mListener); + CachedBluetoothDevice cachedDevice = preference.getCachedDevice(); + if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { + // Only paired device have an associated advanced settings screen + preference.setOnSettingsClickListener(mDeviceProfilesListener); + } } } diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index a978e23..9783fd7 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceCategory; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; @@ -36,7 +37,6 @@ import java.util.WeakHashMap; * * @see BluetoothSettings * @see DevicePickerFragment - * @see BluetoothFindNearby */ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFragment implements BluetoothCallback { @@ -53,7 +53,7 @@ public abstract class DeviceListPreferenceFragment extends LocalBluetoothAdapter mLocalAdapter; LocalBluetoothManager mLocalManager; - private PreferenceCategory mDeviceList; + private PreferenceGroup mDeviceListGroup; final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap = new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>(); @@ -62,7 +62,7 @@ public abstract class DeviceListPreferenceFragment extends mFilter = BluetoothDeviceFilter.ALL_FILTER; } - DeviceListPreferenceFragment(BluetoothDeviceFilter.Filter filter) { + final void setFilter(BluetoothDeviceFilter.Filter filter) { mFilter = filter; } @@ -83,10 +83,11 @@ public abstract class DeviceListPreferenceFragment extends addPreferencesForActivity(); - mDeviceList = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST); - if (mDeviceList == null) { - Log.e(TAG, "Could not find device list preference object!"); - } + mDeviceListGroup = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST); + } + + void setDeviceListGroup(PreferenceGroup preferenceGroup) { + mDeviceListGroup = preferenceGroup; } /** Add preferences from the subclass. */ @@ -105,16 +106,18 @@ public abstract class DeviceListPreferenceFragment extends @Override public void onPause() { super.onPause(); - - mLocalAdapter.stopScanning(); + removeAllDevices(); mLocalManager.setForegroundActivity(null); mLocalManager.getEventManager().unregisterCallback(this); + } + void removeAllDevices() { + mLocalAdapter.stopScanning(); mDevicePreferenceMap.clear(); - mDeviceList.removeAll(); + mDeviceListGroup.removeAll(); } - void addDevices() { + void addCachedDevices() { Collection<CachedBluetoothDevice> cachedDevices = mLocalManager.getCachedDeviceManager().getCachedDevicesCopy(); for (CachedBluetoothDevice cachedDevice : cachedDevices) { @@ -132,7 +135,7 @@ public abstract class DeviceListPreferenceFragment extends } if (preference instanceof BluetoothDevicePreference) { - BluetoothDevicePreference btPreference = (BluetoothDevicePreference)preference; + BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference; CachedBluetoothDevice device = btPreference.getCachedDevice(); mSelectedDevice = device.getDevice(); onDevicePreferenceClick(btPreference); @@ -152,6 +155,9 @@ public abstract class DeviceListPreferenceFragment extends return; } + // Prevent updates while the list shows one of the state messages + if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return; + if (mFilter.matches(cachedDevice.getDevice())) { createDevicePreference(cachedDevice); } @@ -162,7 +168,7 @@ public abstract class DeviceListPreferenceFragment extends getActivity(), cachedDevice); initDevicePreference(preference); - mDeviceList.addPreference(preference); + mDeviceListGroup.addPreference(preference); mDevicePreferenceMap.put(cachedDevice, preference); } @@ -170,13 +176,14 @@ public abstract class DeviceListPreferenceFragment extends * Overridden in {@link BluetoothSettings} to add a listener. * @param preference the newly added preference */ - void initDevicePreference(BluetoothDevicePreference preference) { } + void initDevicePreference(BluetoothDevicePreference preference) { + // Does nothing by default + } public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { - BluetoothDevicePreference preference = mDevicePreferenceMap.remove( - cachedDevice); + BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice); if (preference != null) { - mDeviceList.removePreference(preference); + mDeviceListGroup.removePreference(preference); } } @@ -185,8 +192,8 @@ public abstract class DeviceListPreferenceFragment extends } private void updateProgressUi(boolean start) { - if (mDeviceList instanceof ProgressCategory) { - ((ProgressCategory) mDeviceList).setProgress(start); + if (mDeviceListGroup instanceof ProgressCategory) { + ((ProgressCategory) mDeviceListGroup).setProgress(start); } } diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java index 126df02..8b32941 100644 --- a/src/com/android/settings/bluetooth/DevicePickerFragment.java +++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java @@ -55,7 +55,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { @Override public void onResume() { super.onResume(); - addDevices(); + addCachedDevices(); mLocalAdapter.startScanning(true); } @@ -89,7 +89,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { super.onBluetoothStateChanged(bluetoothState); if (bluetoothState == BluetoothAdapter.STATE_ON) { - mLocalAdapter.startScanning(false); + mLocalAdapter.startScanning(false); } } diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java index 9db4baf..ecb7112 100644 --- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java +++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java @@ -50,7 +50,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment private static final String KEY_RENAME_DEVICE = "rename_device"; private static final String KEY_PROFILE_CONTAINER = "profile_container"; private static final String KEY_UNPAIR = "unpair"; - private static final String KEY_ALLOW_INCOMING = "allow_incoming"; + //private static final String KEY_ALLOW_INCOMING = "allow_incoming"; public static final String EXTRA_DEVICE = "device"; @@ -355,6 +355,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment mCachedDevice.unpair(); } + /* private void setIncomingFileTransfersAllowed(boolean allow) { // TODO: make an IPC call into BluetoothOpp to update Log.d(TAG, "Set allow incoming = " + allow); @@ -364,6 +365,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment // TODO: get this value from BluetoothOpp ??? return true; } + */ private boolean getAutoConnect(LocalBluetoothProfile prof) { return prof.isPreferred(mCachedDevice.getDevice()); |