1 /* 2 * Copyright 2018 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.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.os.SystemProperties; 22 import android.util.AttributeSet; 23 import android.util.Pair; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.preference.Preference; 28 29 import com.android.car.settings.R; 30 import com.android.car.settings.common.MultiActionPreference; 31 import com.android.car.settings.common.ToggleButtonActionItem; 32 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 33 34 /** 35 * Preference which represents a specific {@link CachedBluetoothDevice}. The title, icon, and 36 * summary are kept in sync with the device when the preference is shown. When the device is busy, 37 * the preference is disabled. The equality and sort order of this preference is determined by the 38 * underlying cached device {@link CachedBluetoothDevice#equals(Object)} and {@link 39 * CachedBluetoothDevice#compareTo(CachedBluetoothDevice)}. If two devices are considered equal, the 40 * default preference sort ordering is used (see {@link #compareTo(Preference)}. 41 */ 42 public class BluetoothDevicePreference extends MultiActionPreference { 43 private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = 44 "persist.bluetooth.showdeviceswithoutnames"; 45 46 private final CachedBluetoothDevice mCachedDevice; 47 private final boolean mShowDevicesWithoutNames; 48 private final boolean mShowDisconnectedStateSubtitle; 49 private final CachedBluetoothDevice.Callback mDeviceCallback = this::refreshUi; 50 51 private UpdateToggleButtonListener mUpdateToggleButtonListener; 52 BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice)53 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) { 54 this(context, cachedDevice, /* showDisconnectedStateSubtitle= */ true); 55 } 56 BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, boolean showDisconnectedStateSubtitle)57 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, 58 boolean showDisconnectedStateSubtitle) { 59 super(context); 60 mCachedDevice = cachedDevice; 61 mShowDisconnectedStateSubtitle = showDisconnectedStateSubtitle; 62 mShowDevicesWithoutNames = SystemProperties.getBoolean( 63 BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false); 64 } 65 66 @Override init(@ullable AttributeSet attrs)67 protected void init(@Nullable AttributeSet attrs) { 68 mActionItemArray[0] = new ToggleButtonActionItem(this); 69 mActionItemArray[1] = new ToggleButtonActionItem(this); 70 mActionItemArray[2] = new ToggleButtonActionItem(this); 71 super.init(attrs); 72 73 // Hide actions by default. 74 mActionItemArray[0].setVisible(false); 75 mActionItemArray[1].setVisible(false); 76 mActionItemArray[2].setVisible(false); 77 } 78 79 /** 80 * Returns the {@link CachedBluetoothDevice} represented by this preference. 81 */ getCachedDevice()82 public CachedBluetoothDevice getCachedDevice() { 83 return mCachedDevice; 84 } 85 86 /** 87 * Sets the {@link UpdateToggleButtonListener} that will be called when the toggle buttons 88 * may need to change state. 89 */ setToggleButtonUpdateListener(UpdateToggleButtonListener listener)90 public void setToggleButtonUpdateListener(UpdateToggleButtonListener listener) { 91 mUpdateToggleButtonListener = listener; 92 } 93 94 @Override onAttached()95 public void onAttached() { 96 super.onAttached(); 97 mCachedDevice.registerCallback(mDeviceCallback); 98 refreshUi(); 99 } 100 101 @Override onDetached()102 public void onDetached() { 103 super.onDetached(); 104 mCachedDevice.unregisterCallback(mDeviceCallback); 105 } 106 refreshUi()107 private void refreshUi() { 108 setTitle(mCachedDevice.getName()); 109 setSummary(mCachedDevice.getCarConnectionSummary(/* shortSummary= */ true, 110 mShowDisconnectedStateSubtitle)); 111 112 Pair<Drawable, String> pair = com.android.settingslib.bluetooth.BluetoothUtils 113 .getBtClassDrawableWithDescription(getContext(), mCachedDevice); 114 if (pair.first != null) { 115 setIcon(pair.first); 116 getIcon().setTintList(getContext().getColorStateList(R.color.icon_color_default)); 117 } 118 119 setEnabled(!mCachedDevice.isBusy()); 120 setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName()); 121 122 if (mUpdateToggleButtonListener != null) { 123 mUpdateToggleButtonListener.updateToggleButtonState(this); 124 } 125 // Notify since the ordering may have changed. 126 notifyHierarchyChanged(); 127 } 128 129 @Override equals(Object o)130 public boolean equals(Object o) { 131 if (!(o instanceof BluetoothDevicePreference)) { 132 return false; 133 } 134 return mCachedDevice.equals(((BluetoothDevicePreference) o).mCachedDevice); 135 } 136 137 @Override hashCode()138 public int hashCode() { 139 return mCachedDevice.hashCode(); 140 } 141 142 @Override compareTo(@onNull Preference another)143 public int compareTo(@NonNull Preference another) { 144 if (!(another instanceof BluetoothDevicePreference)) { 145 // Rely on default sort. 146 return super.compareTo(another); 147 } 148 149 return mCachedDevice 150 .compareTo(((BluetoothDevicePreference) another).mCachedDevice); 151 } 152 153 @Override getActionItem(ActionItem actionItem)154 public ToggleButtonActionItem getActionItem(ActionItem actionItem) { 155 switch(actionItem) { 156 case ACTION_ITEM1: 157 return (ToggleButtonActionItem) mActionItemArray[0]; 158 case ACTION_ITEM2: 159 return (ToggleButtonActionItem) mActionItemArray[1]; 160 case ACTION_ITEM3: 161 return (ToggleButtonActionItem) mActionItemArray[2]; 162 default: 163 throw new IllegalArgumentException("Invalid button requested"); 164 } 165 } 166 167 /** 168 * Callback for when toggle buttons may need to be updated 169 */ 170 public interface UpdateToggleButtonListener { 171 /** 172 * Preference state has changed and toggle button changes should be handled. 173 * 174 * @param preference the preference that has been changed 175 */ updateToggleButtonState(BluetoothDevicePreference preference)176 void updateToggleButtonState(BluetoothDevicePreference preference); 177 } 178 } 179