diff options
| author | 2024-03-30 15:24:11 +0000 | |
|---|---|---|
| committer | 2024-03-30 15:24:11 +0000 | |
| commit | 9c699ddfd96b03efa1c7662f0a85a9132e6533c6 (patch) | |
| tree | aed7aeb2f4f447f991e9ff0b79aa56d35a335760 | |
| parent | 0aa7287f01c2b5dc649be1f69beec803cf74a8bd (diff) | |
| parent | 2bf731033cb00f3cb6868d7ffbf05b59e7589d09 (diff) | |
Merge "Implement Hearing Devices Quick Settings Tile (4/n)" into main
9 files changed, 535 insertions, 8 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 09b1eafe4d78..57fcc7462a65 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -542,6 +542,25 @@ public class BluetoothUtils { } /** + * Checks if the Bluetooth device is an available hearing device, which means: + * 1) currently connected + * 2) is Hearing Aid + * 3) connected profile match hearing aid related profiles (e.g. ASHA, HAP) + * + * @param cachedDevice the CachedBluetoothDevice + * @return if the device is Available hearing device + */ + @WorkerThread + public static boolean isAvailableHearingDevice(CachedBluetoothDevice cachedDevice) { + if (isDeviceConnected(cachedDevice) && cachedDevice.isConnectedHearingAidDevice()) { + Log.d(TAG, "isFilterMatched() device : " + + cachedDevice.getName() + ", the profile is connected."); + return true; + } + return false; + } + + /** * Check if the Bluetooth device is a ConnectedBluetoothDevice, which means: * 1) currently connected * 2) is not Hearing Aid or LE Audio diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 475a6d65a845..1246fd85ee16 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -430,4 +430,14 @@ public class BluetoothUtilsTest { assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice)).isEqualTo(true); } + + @Test + public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() { + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mBluetoothDevice.isConnected()).thenReturn(true); + + assertThat(BluetoothUtils.isAvailableHearingDevice(mCachedBluetoothDevice)).isEqualTo(true); + } } diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml index 8ceda9851314..a5cdaeb2f925 100644 --- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml +++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml @@ -22,6 +22,14 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/device_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/pair_new_device_button" /> + <Button android:id="@+id/pair_new_device_button" style="@style/BluetoothTileDialog.Device" @@ -33,7 +41,7 @@ android:contentDescription="@string/accessibility_hearing_device_pair_new_device" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintTop_toBottomOf="@id/device_list" android:drawableStart="@drawable/ic_add" android:drawablePadding="20dp" android:drawableTint="?android:attr/textColorPrimary" diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 6fbf3dd0d63f..96eb4b3a4c3f 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -19,18 +19,38 @@ package com.android.systemui.accessibility.hearingaid; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static java.util.Collections.emptyList; + +import android.content.Context; import android.content.Intent; +import android.media.AudioManager; import android.os.Bundle; +import android.os.Handler; import android.provider.Settings; import android.view.LayoutInflater; +import android.view.View; import android.view.View.Visibility; import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback; import com.android.systemui.animation.DialogTransitionAnimator; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.tiles.dialog.bluetooth.ActiveHearingDeviceItemFactory; +import com.android.systemui.qs.tiles.dialog.bluetooth.AvailableHearingDeviceItemFactory; +import com.android.systemui.qs.tiles.dialog.bluetooth.ConnectedDeviceItemFactory; +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem; +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItemFactory; +import com.android.systemui.qs.tiles.dialog.bluetooth.SavedHearingDeviceItemFactory; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -38,18 +58,44 @@ import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + /** * Dialog for showing hearing devices controls. */ -public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate { +public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, + HearingDeviceItemCallback, BluetoothCallback { + @VisibleForTesting + static final String ACTION_BLUETOOTH_DEVICE_DETAILS = + "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"; + private static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"; + private static final String KEY_BLUETOOTH_ADDRESS = "device_address"; private final SystemUIDialog.Factory mSystemUIDialogFactory; private final DialogTransitionAnimator mDialogTransitionAnimator; private final ActivityStarter mActivityStarter; private final boolean mShowPairNewDevice; + private final LocalBluetoothManager mLocalBluetoothManager; + private final Handler mMainHandler; + private final AudioManager mAudioManager; + private HearingDevicesListAdapter mDeviceListAdapter; private SystemUIDialog mDialog; + private RecyclerView mDeviceList; private Button mPairButton; + private final List<DeviceItemFactory> mHearingDeviceItemFactoryList = List.of( + new ActiveHearingDeviceItemFactory(), + new AvailableHearingDeviceItemFactory(), + // TODO(b/331305850): setHearingAidInfo() for connected but not connect to profile + // hearing device only called from + // settings/bluetooth/DeviceListPreferenceFragment#handleLeScanResult, so we don't know + // it is connected but not yet connect to profile hearing device in systemui. + // Show all connected but not connect to profile bluetooth device for now. + new ConnectedDeviceItemFactory(), + new SavedHearingDeviceItemFactory() + ); /** Factory to create a {@link HearingDevicesDialogDelegate} dialog instance. */ @AssistedFactory @@ -64,11 +110,17 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate { @Assisted boolean showPairNewDevice, SystemUIDialog.Factory systemUIDialogFactory, ActivityStarter activityStarter, - DialogTransitionAnimator dialogTransitionAnimator) { + DialogTransitionAnimator dialogTransitionAnimator, + @Nullable LocalBluetoothManager localBluetoothManager, + @Main Handler handler, + AudioManager audioManager) { mShowPairNewDevice = showPairNewDevice; mSystemUIDialogFactory = systemUIDialogFactory; mActivityStarter = activityStarter; mDialogTransitionAnimator = dialogTransitionAnimator; + mLocalBluetoothManager = localBluetoothManager; + mMainHandler = handler; + mAudioManager = audioManager; } @Override @@ -81,6 +133,47 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate { } @Override + public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) { + dismissDialogIfExists(); + Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS); + Bundle bundle = new Bundle(); + bundle.putString(KEY_BLUETOOTH_ADDRESS, deviceItem.getCachedBluetoothDevice().getAddress()); + intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0, + mDialogTransitionAnimator.createActivityTransitionController(view)); + } + + @Override + public void onDeviceItemOnClicked(@NonNull DeviceItem deviceItem, @NonNull View view) { + CachedBluetoothDevice cachedBluetoothDevice = deviceItem.getCachedBluetoothDevice(); + switch (deviceItem.getType()) { + case ACTIVE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE -> + cachedBluetoothDevice.disconnect(); + case AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> cachedBluetoothDevice.setActive(); + case SAVED_BLUETOOTH_DEVICE -> cachedBluetoothDevice.connect(); + } + } + + @Override + public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice, + int bluetoothProfile) { + mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(getHearingDevicesList())); + } + + @Override + public void onProfileConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice, + int state, int bluetoothProfile) { + mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(getHearingDevicesList())); + } + + @Override + public void onAclConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice, + int state) { + mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(getHearingDevicesList())); + } + + @Override public void beforeCreate(@NonNull SystemUIDialog dialog, @Nullable Bundle savedInstanceState) { dialog.setTitle(R.string.quick_settings_hearing_devices_dialog_title); dialog.setView(LayoutInflater.from(dialog.getContext()).inflate( @@ -95,10 +188,34 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate { @Override public void onCreate(@NonNull SystemUIDialog dialog, @Nullable Bundle savedInstanceState) { mPairButton = dialog.requireViewById(R.id.pair_new_device_button); + mDeviceList = dialog.requireViewById(R.id.device_list); + setupDeviceListView(dialog); setupPairNewDeviceButton(dialog, mShowPairNewDevice ? VISIBLE : GONE); } + @Override + public void onStart(@NonNull SystemUIDialog dialog) { + if (mLocalBluetoothManager == null) { + return; + } + mLocalBluetoothManager.getEventManager().registerCallback(this); + } + + @Override + public void onStop(@NonNull SystemUIDialog dialog) { + if (mLocalBluetoothManager == null) { + return; + } + mLocalBluetoothManager.getEventManager().unregisterCallback(this); + } + + private void setupDeviceListView(SystemUIDialog dialog) { + mDeviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext())); + mDeviceListAdapter = new HearingDevicesListAdapter(getHearingDevicesList(), this); + mDeviceList.setAdapter(mDeviceListAdapter); + } + private void setupPairNewDeviceButton(SystemUIDialog dialog, @Visibility int visibility) { if (visibility == VISIBLE) { mPairButton.setOnClickListener(v -> { @@ -113,6 +230,31 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate { } } + private List<DeviceItem> getHearingDevicesList() { + if (mLocalBluetoothManager == null + || !mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) { + return emptyList(); + } + + return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream() + .map(this::createHearingDeviceItem) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private DeviceItem createHearingDeviceItem(CachedBluetoothDevice cachedDevice) { + final Context context = mDialog.getContext(); + if (cachedDevice == null) { + return null; + } + for (DeviceItemFactory itemFactory : mHearingDeviceItemFactoryList) { + if (itemFactory.isFilterMatched(context, cachedDevice, mAudioManager)) { + return itemFactory.create(context, cachedDevice); + } + } + return null; + } + private void dismissDialogIfExists() { if (mDialog != null) { mDialog.dismiss(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java new file mode 100644 index 000000000000..695d04f5df9d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2024 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.systemui.accessibility.hearingaid; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem; +import com.android.systemui.res.R; + +import kotlin.Pair; + +import java.util.List; + +/** + * Adapter for showing hearing device item list {@link DeviceItem}. + */ +public class HearingDevicesListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + private final List<DeviceItem> mItemList; + private final HearingDeviceItemCallback mCallback; + + public HearingDevicesListAdapter(List<DeviceItem> itemList, + HearingDeviceItemCallback callback) { + mItemList = itemList; + mCallback = callback; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) { + View view = LayoutInflater.from(viewGroup.getContext()).inflate( + R.layout.bluetooth_device_item, viewGroup, false); + return new DeviceItemViewHolder(view, viewGroup.getContext()); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + DeviceItem item = mItemList.get(position); + ((DeviceItemViewHolder) viewHolder).bindView(item, mCallback); + } + + @Override + public int getItemCount() { + return mItemList.size(); + } + + /** + * Updates items in the adapter. + * + * @param itemList bluetooth device item list + */ + public void refreshDeviceItemList(List<DeviceItem> itemList) { + mItemList.clear(); + mItemList.addAll(itemList); + notifyDataSetChanged(); + } + + /** + * Interface to provide callbacks when click on the device item in hearing device quick + * settings tile. + */ + public interface HearingDeviceItemCallback { + /** + * Called when gear view in device item is clicked. + * + * @param deviceItem bluetooth device item + * @param view the view that was clicked + */ + void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view); + + /** + * Called when device item is clicked. + * + * @param deviceItem bluetooth device item + * @param view the view that was clicked + */ + void onDeviceItemOnClicked(@NonNull DeviceItem deviceItem, @NonNull View view); + } + + private static class DeviceItemViewHolder extends RecyclerView.ViewHolder { + private final Context mContext; + private final View mContainer; + private final TextView mNameView; + private final TextView mSummaryView; + private final ImageView mIconView; + private final View mGearView; + + DeviceItemViewHolder(@NonNull View itemView, Context context) { + super(itemView); + mContext = context; + mContainer = itemView.requireViewById(R.id.bluetooth_device_row); + mNameView = itemView.requireViewById(R.id.bluetooth_device_name); + mSummaryView = itemView.requireViewById(R.id.bluetooth_device_summary); + mIconView = itemView.requireViewById(R.id.bluetooth_device_icon); + mGearView = itemView.requireViewById(R.id.gear_icon); + } + + public void bindView(DeviceItem item, HearingDeviceItemCallback callback) { + mContainer.setEnabled(item.isEnabled()); + mContainer.setOnClickListener(view -> callback.onDeviceItemOnClicked(item, view)); + Integer backgroundResId = item.getBackground(); + if (backgroundResId != null) { + mContainer.setBackground(mContext.getDrawable(item.getBackground())); + } + mNameView.setText(item.getDeviceName()); + mSummaryView.setText(item.getConnectionSummary()); + Pair<Drawable, String> iconPair = item.getIconWithDescription(); + if (iconPair != null) { + mIconView.setImageDrawable(iconPair.getFirst()); + mIconView.setContentDescription(iconPair.getSecond()); + } + mGearView.setOnClickListener(view -> callback.onDeviceItemGearClicked(item, view)); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt index f13ecf3e91b9..56ba07941e4d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt @@ -45,7 +45,7 @@ internal abstract class DeviceItemFactory { abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem } -internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() { +internal open class ActiveMediaDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, @@ -72,7 +72,18 @@ internal class ActiveMediaDeviceItemFactory : DeviceItemFactory() { } } -internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() { +internal class ActiveHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() { + override fun isFilterMatched( + context: Context, + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return BluetoothUtils.isActiveMediaDevice(cachedDevice) && + BluetoothUtils.isAvailableHearingDevice(cachedDevice) + } +} + +internal open class AvailableMediaDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, @@ -101,6 +112,17 @@ internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() { } } +internal class AvailableHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() { + override fun isFilterMatched( + context: Context, + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return !BluetoothUtils.isActiveMediaDevice(cachedDevice) && + BluetoothUtils.isAvailableHearingDevice(cachedDevice) + } +} + internal class ConnectedDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, @@ -135,7 +157,7 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { } } -internal class SavedDeviceItemFactory : DeviceItemFactory() { +internal open class SavedDeviceItemFactory : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, @@ -168,3 +190,25 @@ internal class SavedDeviceItemFactory : DeviceItemFactory() { ) } } + +internal class SavedHearingDeviceItemFactory : SavedDeviceItemFactory() { + override fun isFilterMatched( + context: Context, + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager? + ): Boolean { + return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { + !BluetoothUtils.isExclusivelyManagedBluetoothDevice( + context, + cachedDevice.getDevice() + ) && + cachedDevice.isHearingAidDevice && + cachedDevice.bondState == BluetoothDevice.BOND_BONDED && + !cachedDevice.isConnected + } else { + cachedDevice.isHearingAidDevice && + cachedDevice.bondState == BluetoothDevice.BOND_BONDED && + !cachedDevice.isConnected + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt index 1df496b17aa5..fce25ec68190 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt @@ -137,7 +137,6 @@ constructor( ?.create(context, cachedDevice) } .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices) - // Only emit when the job is not cancelled if (isActive) { mutableDeviceItemUpdate.tryEmit(deviceItems) diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index 46010d5f8c73..b0f0363a3e97 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -27,6 +27,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Intent; +import android.media.AudioManager; +import android.os.Handler; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -34,10 +36,17 @@ import android.view.View; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.BluetoothEventManager; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem; +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItemType; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -51,6 +60,9 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; +import java.util.List; + /** Tests for {@link HearingDevicesDialogDelegate}. */ @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -59,6 +71,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); + private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"; + @Mock private SystemUIDialog.Factory mSystemUIDialogFactory; @Mock @@ -69,12 +83,36 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { private DialogTransitionAnimator mDialogTransitionAnimator; @Mock private ActivityStarter mActivityStarter; + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private LocalBluetoothAdapter mLocalBluetoothAdapter; + @Mock + private CachedBluetoothDeviceManager mCachedDeviceManager; + @Mock + private BluetoothEventManager mBluetoothEventManager; + @Mock + private AudioManager mAudioManager; + @Mock + private CachedBluetoothDevice mCachedDevice; + @Mock + private DeviceItem mHearingDeviceItem; private SystemUIDialog mDialog; private HearingDevicesDialogDelegate mDialogDelegate; + private TestableLooper mTestableLooper; + private final List<CachedBluetoothDevice> mDevices = new ArrayList<>(); @Before public void setUp() { + mTestableLooper = TestableLooper.get(this); + when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter); + when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); + when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices); + when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager); when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); + when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS); + when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice); setUpPairNewDeviceDialog(); @@ -100,12 +138,60 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS); } + @Test + public void onDeviceItemGearClicked_intentActionMatch() { + setUpDeviceListDialog(); + + mDialogDelegate.onDeviceItemGearClicked(mHearingDeviceItem, new View(mContext)); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mActivityStarter).postStartActivityDismissingKeyguard(intentCaptor.capture(), + anyInt(), any()); + assertThat(intentCaptor.getValue().getAction()).isEqualTo( + HearingDevicesDialogDelegate.ACTION_BLUETOOTH_DEVICE_DETAILS); + + } + + @Test + public void onDeviceItemOnClicked_connectedDevice_disconnect() { + when(mHearingDeviceItem.getType()).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE); + + mDialogDelegate.onDeviceItemOnClicked(mHearingDeviceItem, new View(mContext)); + + verify(mCachedDevice).disconnect(); + } + private void setUpPairNewDeviceDialog() { mDialogDelegate = new HearingDevicesDialogDelegate( true, mSystemUIDialogFactory, mActivityStarter, - mDialogTransitionAnimator + mDialogTransitionAnimator, + mLocalBluetoothManager, + new Handler(mTestableLooper.getLooper()), + mAudioManager + ); + mDialog = new SystemUIDialog( + mContext, + 0, + DEFAULT_DISMISS_ON_DEVICE_LOCK, + mSystemUIDialogManager, + mSysUiState, + getFakeBroadcastDispatcher(), + mDialogTransitionAnimator, + mDialogDelegate + ); + } + + private void setUpDeviceListDialog() { + mDialogDelegate = new HearingDevicesDialogDelegate( + false, + mSystemUIDialogFactory, + mActivityStarter, + mDialogTransitionAnimator, + mLocalBluetoothManager, + new Handler(mTestableLooper.getLooper()), + mAudioManager ); mDialog = new SystemUIDialog( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java new file mode 100644 index 000000000000..95d5597ef645 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 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.systemui.accessibility.hearingaid; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.qs.tiles.dialog.bluetooth.DeviceItem; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +/** Tests for {@link HearingDevicesListAdapter}. */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class HearingDevicesListAdapterTest extends SysuiTestCase { + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"; + + @Mock + private DeviceItem mHearingDeviceItem; + @Mock + private CachedBluetoothDevice mCachedDevice; + @Mock + private HearingDevicesListAdapter.HearingDeviceItemCallback mDeviceItemCallback; + private HearingDevicesListAdapter mAdapter; + + @Before + public void setUp() { + when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS); + when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice); + } + + @Test + public void constructor_oneItem_getOneCount() { + mAdapter = new HearingDevicesListAdapter(List.of(mHearingDeviceItem), mDeviceItemCallback); + + assertThat(mAdapter.getItemCount()).isEqualTo(1); + } + + @Test + public void refreshDeviceItemList_oneItem_getOneCount() { + mAdapter = new HearingDevicesListAdapter(new ArrayList<>(), mDeviceItemCallback); + + mAdapter.refreshDeviceItemList(List.of(mHearingDeviceItem)); + + assertThat(mAdapter.getItemCount()).isEqualTo(1); + } +} |