1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.bluetooth; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothDevicePicker; 21 import android.car.drivingstate.CarUxRestrictions; 22 import android.content.Context; 23 import android.content.Intent; 24 25 import com.android.car.settings.R; 26 import com.android.car.settings.common.FragmentController; 27 import com.android.car.settings.common.Logger; 28 import com.android.settingslib.bluetooth.BluetoothDeviceFilter; 29 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 30 31 /** 32 * Displays a list of Bluetooth devices for the user to select. When a device is selected, a 33 * {@link BluetoothDevicePicker#ACTION_DEVICE_SELECTED} broadcast is sent containing {@link 34 * BluetoothDevice#EXTRA_DEVICE}. 35 * 36 * <p>This is useful to other application to obtain a device without needing to implement the UI. 37 * The activity hosting this controller should be launched with an intent as detailed in {@link 38 * BluetoothDevicePicker#ACTION_LAUNCH}. This controller will filter devices as specified by {@link 39 * BluetoothDevicePicker#EXTRA_FILTER_TYPE} and deliver the broadcast to the specified {@link 40 * BluetoothDevicePicker#EXTRA_LAUNCH_PACKAGE} {@link BluetoothDevicePicker#EXTRA_LAUNCH_CLASS} 41 * component. If authentication is required ({@link BluetoothDevicePicker#EXTRA_NEED_AUTH}), this 42 * controller will initiate pairing with the device and send the selected broadcast once the device 43 * successfully pairs. If no device is selected and this controller is destroyed, a broadcast with 44 * a {@code null} {@link BluetoothDevice#EXTRA_DEVICE} is sent. 45 */ 46 public class BluetoothDevicePickerPreferenceController extends 47 BluetoothScanningDevicesGroupPreferenceController { 48 49 private static final Logger LOG = new Logger(BluetoothDevicePickerPreferenceController.class); 50 51 private BluetoothDeviceFilter.Filter mFilter; 52 53 private boolean mNeedAuth; 54 private String mLaunchPackage; 55 private String mLaunchClass; 56 57 private CachedBluetoothDevice mSelectedDevice; 58 BluetoothDevicePickerPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)59 public BluetoothDevicePickerPreferenceController(Context context, String preferenceKey, 60 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 61 super(context, preferenceKey, fragmentController, uxRestrictions); 62 } 63 64 /** 65 * Sets the intent with which {@link BluetoothDevicePickerActivity} was launched. The intent 66 * may contain {@link BluetoothDevicePicker} extras to customize the selection list and specify 67 * the destination of the selected device. See {@link BluetoothDevicePicker#ACTION_LAUNCH}. 68 */ setLaunchIntent(Intent intent)69 public void setLaunchIntent(Intent intent) { 70 mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false); 71 mFilter = BluetoothDeviceFilter.getFilter( 72 intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE, 73 BluetoothDevicePicker.FILTER_TYPE_ALL)); 74 mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE); 75 mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS); 76 } 77 78 @Override checkInitialized()79 protected void checkInitialized() { 80 if (mFilter == null) { 81 throw new IllegalStateException("launch intent must be set"); 82 } 83 } 84 85 @Override getDeviceFilter()86 protected BluetoothDeviceFilter.Filter getDeviceFilter() { 87 return mFilter; 88 } 89 90 @Override onDeviceClickedInternal(CachedBluetoothDevice cachedDevice)91 protected void onDeviceClickedInternal(CachedBluetoothDevice cachedDevice) { 92 mSelectedDevice = cachedDevice; 93 BluetoothUtils.persistSelectedDeviceInPicker(getContext(), cachedDevice.getAddress()); 94 95 if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED || !mNeedAuth) { 96 sendDevicePickedIntent(cachedDevice.getDevice()); 97 getFragmentController().goBack(); 98 return; 99 } 100 101 if (cachedDevice.startPairing()) { 102 LOG.d("startPairing"); 103 } else { 104 BluetoothUtils.showError(getContext(), cachedDevice.getName(), 105 R.string.bluetooth_pairing_error_message); 106 refreshUi(); 107 } 108 } 109 110 @Override onStartInternal()111 protected void onStartInternal() { 112 super.onStartInternal(); 113 mSelectedDevice = null; 114 } 115 116 @Override onDestroyInternal()117 protected void onDestroyInternal() { 118 super.onDestroyInternal(); 119 if (mSelectedDevice == null) { 120 // Notify that no device was selected. 121 sendDevicePickedIntent(null); 122 } 123 } 124 125 @Override onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)126 public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { 127 super.onDeviceBondStateChanged(cachedDevice, bondState); 128 if (bondState == BluetoothDevice.BOND_BONDED && cachedDevice.equals(mSelectedDevice)) { 129 sendDevicePickedIntent(mSelectedDevice.getDevice()); 130 getFragmentController().goBack(); 131 } 132 } 133 sendDevicePickedIntent(BluetoothDevice device)134 private void sendDevicePickedIntent(BluetoothDevice device) { 135 LOG.d("sendDevicePickedIntent device: " + device + " package: " + mLaunchPackage 136 + " class: " + mLaunchClass); 137 Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED); 138 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 139 if (mLaunchPackage != null && mLaunchClass != null) { 140 intent.setClassName(mLaunchPackage, mLaunchClass); 141 } 142 getContext().sendBroadcast(intent); 143 } 144 } 145