diff --git a/res/values/strings.xml b/res/values/strings.xml index cf13dfbf8d2..3f01af0b9cf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -150,14 +150,10 @@ Pair right ear Pair left ear - - For all available hearing devices - - More hearing device settings - - Change cross-device settings like shortcut, and telecoil controls - - For this device + + Hearing device settings + + Shortcut, hearing aid compatibility Audio output diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index d260554f937..91f73a70b6e 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -69,7 +69,7 @@ android:key="device_companion_apps"/> + android:key="hearing_device_group" /> diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceController.java new file mode 100644 index 00000000000..27a4cb17aa1 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceController.java @@ -0,0 +1,102 @@ +/* + * 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.settings.bluetooth; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import com.google.common.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.List; + +/** + * The controller of the hearing device controls. + * + *

Note: It is responsible for creating the sub-controllers inside this preference + * category controller. + */ +public class BluetoothDetailsHearingDeviceController extends BluetoothDetailsController { + static final String KEY_HEARING_DEVICE_GROUP = "hearing_device_group"; + + private final List mControllers = new ArrayList<>(); + private Lifecycle mLifecycle; + + public BluetoothDetailsHearingDeviceController(@NonNull Context context, + @NonNull PreferenceFragmentCompat fragment, + @NonNull CachedBluetoothDevice device, + @NonNull Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + mLifecycle = lifecycle; + } + + @VisibleForTesting + void setSubControllers( + BluetoothDetailsHearingDeviceSettingsController hearingDeviceSettingsController) { + mControllers.clear(); + mControllers.add(hearingDeviceSettingsController); + } + + @Override + public boolean isAvailable() { + return mControllers.stream().anyMatch(BluetoothDetailsController::isAvailable); + } + + @Override + @NonNull + public String getPreferenceKey() { + return KEY_HEARING_DEVICE_GROUP; + } + + @Override + protected void init(PreferenceScreen screen) { + + } + + @Override + protected void refresh() { + + } + + /** + * Initiates the sub controllers controlled by this group controller. + * + *

Note: The caller must call this method when creating this class. + * + * @param isLaunchFromHearingDevicePage a boolean that determines if the caller is launch from + * hearing device page + */ + void initSubControllers(boolean isLaunchFromHearingDevicePage) { + mControllers.clear(); + // Don't need to show the entrance to hearing device page when launched from the same page + if (!isLaunchFromHearingDevicePage) { + mControllers.add(new BluetoothDetailsHearingDeviceSettingsController(mContext, + mFragment, mCachedDevice, mLifecycle)); + } + } + + @NonNull + public List getSubControllers() { + return mControllers; + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsController.java similarity index 75% rename from src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java rename to src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsController.java index 162abc78aef..b381cc4b2b8 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsController.java @@ -16,6 +16,8 @@ package com.android.settings.bluetooth; +import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP; + import android.content.Context; import android.text.TextUtils; @@ -36,15 +38,13 @@ import com.google.common.annotations.VisibleForTesting; /** * The controller of the hearing device settings to launch Hearing device page. */ -public class BluetoothDetailsHearingDeviceControlsController extends BluetoothDetailsController +public class BluetoothDetailsHearingDeviceSettingsController extends BluetoothDetailsController implements Preference.OnPreferenceClickListener { @VisibleForTesting - static final String KEY_DEVICE_CONTROLS_GENERAL_GROUP = "device_controls_general"; - @VisibleForTesting - static final String KEY_HEARING_DEVICE_CONTROLS = "hearing_device_controls"; + static final String KEY_HEARING_DEVICE_SETTINGS = "hearing_device_settings"; - public BluetoothDetailsHearingDeviceControlsController(Context context, + public BluetoothDetailsHearingDeviceSettingsController(Context context, PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { super(context, fragment, device, lifecycle); lifecycle.addObserver(this); @@ -57,37 +57,39 @@ public class BluetoothDetailsHearingDeviceControlsController extends BluetoothDe @Override protected void init(PreferenceScreen screen) { - if (!mCachedDevice.isHearingAidDevice()) { + if (!isAvailable()) { return; } - - final PreferenceCategory prefCategory = screen.findPreference(getPreferenceKey()); - final Preference pref = createHearingDeviceControlsPreference(prefCategory.getContext()); - prefCategory.addPreference(pref); + final PreferenceCategory group = screen.findPreference(KEY_HEARING_DEVICE_GROUP); + final Preference pref = createHearingDeviceSettingsPreference(group.getContext()); + group.addPreference(pref); } @Override - protected void refresh() {} + protected void refresh() { + + } @Override public String getPreferenceKey() { - return KEY_DEVICE_CONTROLS_GENERAL_GROUP; + return KEY_HEARING_DEVICE_SETTINGS; } @Override public boolean onPreferenceClick(Preference preference) { - if (TextUtils.equals(preference.getKey(), KEY_HEARING_DEVICE_CONTROLS)) { + if (TextUtils.equals(preference.getKey(), KEY_HEARING_DEVICE_SETTINGS)) { launchAccessibilityHearingDeviceSettings(); return true; } return false; } - private Preference createHearingDeviceControlsPreference(Context context) { + private Preference createHearingDeviceSettingsPreference(Context context) { final ArrowPreference preference = new ArrowPreference(context); - preference.setKey(KEY_HEARING_DEVICE_CONTROLS); - preference.setTitle(context.getString(R.string.bluetooth_device_controls_title)); - preference.setSummary(context.getString(R.string.bluetooth_device_controls_summary)); + preference.setKey(KEY_HEARING_DEVICE_SETTINGS); + preference.setTitle(context.getString(R.string.bluetooth_hearing_device_settings_title)); + preference.setSummary( + context.getString(R.string.bluetooth_hearing_device_settings_summary)); preference.setOnPreferenceClickListener(this); return preference; diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index 9c68c9cc870..dae1e086115 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -326,16 +326,16 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment lifecycle)); controllers.add(new BluetoothDetailsPairOtherController(context, this, mCachedDevice, lifecycle)); - // Don't need to show hearing device again when launched from the same page. - if (!isLaunchFromHearingDevicePage()) { - controllers.add(new BluetoothDetailsHearingDeviceControlsController(context, this, - mCachedDevice, lifecycle)); - } - controllers.add(new BluetoothDetailsDataSyncController(context, this, - mCachedDevice, lifecycle)); - controllers.add( - new BluetoothDetailsExtraOptionsController( - context, this, mCachedDevice, lifecycle)); + controllers.add(new BluetoothDetailsDataSyncController(context, this, mCachedDevice, + lifecycle)); + controllers.add(new BluetoothDetailsExtraOptionsController(context, this, mCachedDevice, + lifecycle)); + BluetoothDetailsHearingDeviceController hearingDeviceController = + new BluetoothDetailsHearingDeviceController(context, this, mCachedDevice, + lifecycle); + controllers.add(hearingDeviceController); + hearingDeviceController.initSubControllers(isLaunchFromHearingDevicePage()); + controllers.addAll(hearingDeviceController.getSubControllers()); } return controllers; } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControllerTest.java new file mode 100644 index 00000000000..d5284b4c438 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControllerTest.java @@ -0,0 +1,83 @@ +/* + * 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.settings.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +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 org.robolectric.RobolectricTestRunner; + +/** Tests for {@link BluetoothDetailsHearingDeviceController}. */ +@RunWith(RobolectricTestRunner.class) +public class BluetoothDetailsHearingDeviceControllerTest extends + BluetoothDetailsControllerTestBase { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private BluetoothDetailsHearingDeviceController mHearingDeviceController; + + @Mock + private BluetoothDetailsHearingDeviceSettingsController mHearingDeviceSettingsController; + + @Override + public void setUp() { + super.setUp(); + + mHearingDeviceController = new BluetoothDetailsHearingDeviceController(mContext, + mFragment, mCachedDevice, mLifecycle); + mHearingDeviceController.setSubControllers(mHearingDeviceSettingsController); + } + + @Test + public void isAvailable_hearingDeviceSettingsAvailable_returnTrue() { + when(mHearingDeviceSettingsController.isAvailable()).thenReturn(true); + + assertThat(mHearingDeviceController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_noControllersAvailable_returnFalse() { + when(mHearingDeviceSettingsController.isAvailable()).thenReturn(false); + + assertThat(mHearingDeviceController.isAvailable()).isFalse(); + } + + + @Test + public void initSubControllers_launchFromHearingDevicePage_hearingDeviceSettingsNotExist() { + mHearingDeviceController.initSubControllers(true); + + assertThat(mHearingDeviceController.getSubControllers().stream().anyMatch( + c -> c instanceof BluetoothDetailsHearingDeviceSettingsController)).isFalse(); + } + + @Test + public void initSubControllers_notLaunchFromHearingDevicePage_hearingDeviceSettingsExist() { + mHearingDeviceController.initSubControllers(false); + + assertThat(mHearingDeviceController.getSubControllers().stream().anyMatch( + c -> c instanceof BluetoothDetailsHearingDeviceSettingsController)).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsControllerTest.java similarity index 81% rename from tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java rename to tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsControllerTest.java index 364d299e519..b420717d397 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceSettingsControllerTest.java @@ -39,23 +39,24 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -/** Tests for {@link BluetoothDetailsHearingDeviceControlsController}. */ +/** Tests for {@link BluetoothDetailsHearingDeviceSettingsController}. */ @RunWith(RobolectricTestRunner.class) -public class BluetoothDetailsHearingDeviceControlsControllerTest extends +public class BluetoothDetailsHearingDeviceSettingsControllerTest extends BluetoothDetailsControllerTestBase { + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @Captor private ArgumentCaptor mIntentArgumentCaptor; - private BluetoothDetailsHearingDeviceControlsController mController; + private BluetoothDetailsHearingDeviceSettingsController mController; @Override public void setUp() { super.setUp(); FakeFeatureFactory.setupForTest(); - mController = new BluetoothDetailsHearingDeviceControlsController(mActivity, mFragment, + mController = new BluetoothDetailsHearingDeviceSettingsController(mActivity, mFragment, mCachedDevice, mLifecycle); when(mCachedDevice.isHearingAidDevice()).thenReturn(true); } @@ -75,12 +76,12 @@ public class BluetoothDetailsHearingDeviceControlsControllerTest extends } @Test - public void onPreferenceClick_hearingDeviceControlsKey_LaunchExpectedFragment() { - final Preference hearingControlsKeyPreference = new Preference(mContext); - hearingControlsKeyPreference.setKey( - BluetoothDetailsHearingDeviceControlsController.KEY_HEARING_DEVICE_CONTROLS); + public void onPreferenceClick_hearingDeviceSettingsKey_launchExpectedFragment() { + final Preference hearingDeviceSettingsPreference = new Preference(mContext); + hearingDeviceSettingsPreference.setKey( + BluetoothDetailsHearingDeviceSettingsController.KEY_HEARING_DEVICE_SETTINGS); - mController.onPreferenceClick(hearingControlsKeyPreference); + mController.onPreferenceClick(hearingDeviceSettingsPreference); assertStartActivityWithExpectedFragment(mActivity, AccessibilityHearingAidsFragment.class.getName()); diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java index fc72c412b6e..50aa7719ccb 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java @@ -18,7 +18,7 @@ package com.android.settings.bluetooth; import static android.bluetooth.BluetoothDevice.BOND_NONE; -import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceControlsController.KEY_DEVICE_CONTROLS_GENERAL_GROUP; +import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceSettingsController.KEY_HEARING_DEVICE_SETTINGS; import static com.google.common.truth.Truth.assertThat; @@ -237,7 +237,7 @@ public class BluetoothDeviceDetailsFragmentTest { assertThat(controllerList.stream() .anyMatch(controller -> controller.getPreferenceKey().equals( - KEY_DEVICE_CONTROLS_GENERAL_GROUP))).isFalse(); + KEY_HEARING_DEVICE_SETTINGS))).isFalse(); } @Test @@ -253,7 +253,7 @@ public class BluetoothDeviceDetailsFragmentTest { assertThat(controllerList.stream() .anyMatch(controller -> controller.getPreferenceKey().equals( - KEY_DEVICE_CONTROLS_GENERAL_GROUP))).isTrue(); + KEY_HEARING_DEVICE_SETTINGS))).isTrue(); } private InputDevice createInputDeviceWithMatchingBluetoothAddress() {