Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1258059f authored by jasonwshsu's avatar jasonwshsu
Browse files

[HA Input] Add UI to support hearing device microphone change ability in device details page

In this patch:
* Add custom dialog preference: HearingDeviceInputRoutingPreference and its controller
* Contain radio group to show 'hearing device microphone' and 'this phone's microphone' for user's preference
* set/get user's preference via BluetoothDevice#isMicrophonePreferredForCalls, BluetoothDevicwe#setMicrophonePreferredForCalls
* check if support HapProfile and if in AudioManager#getDevice(GET_DEVICES_INPUTS) list

Bug: 349255906
Test: atest HearingDeviceInputRoutingPreferenceTest BluetoothDetailsHearingDeviceInputRoutingControllerTest BluetoothDetailsHearingDeviceControllerTest
Flag: com.android.settingslib.flags.hearing_devices_input_routing_control
Change-Id: I2e4dbc7fb98353ed52d0d175df4e8725df6b9a05
parent 597b3c64
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     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.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="?android:attr/dialogPreferredPadding"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textDirection="locale"
        android:textAppearance="?android:attr/textAppearanceListItemSecondary"
        android:text="@string/bluetooth_hearing_device_input_routing_dialog_summary" />

    <RadioGroup
        android:id="@+id/input_routing_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:checkedButton="@id/input_from_hearing_device">
        <RadioButton
            android:id="@+id/input_from_hearing_device"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:minHeight="?android:attr/listPreferredItemHeight"
            android:text="@string/bluetooth_hearing_device_input_routing_hearing_device_option"/>
        <RadioButton
            android:id="@+id/input_from_builtin_mic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:minHeight="?android:attr/listPreferredItemHeight"
            android:text="@string/bluetooth_hearing_device_input_routing_builtin_option"/>
    </RadioGroup>

</LinearLayout>
+10 −0
Original line number Diff line number Diff line
@@ -180,6 +180,16 @@
    <string name="bluetooth_ambient_volume_unmute">Unmute surroundings</string>
    <!-- Message when changing ambient state failed. [CHAR LIMIT=NONE] -->
    <string name="bluetooth_ambient_volume_error">Couldn\u2019t update surroundings</string>
    <!-- Connected devices settings. Title for hearing device input routing control preference. -->
    <string name="bluetooth_hearing_device_input_routing_title">Default microphone for calls</string>
    <!-- Connected devices settings. Title for hearing device input routing control dialog. -->
    <string name="bluetooth_hearing_device_input_routing_dialog_title">Default microphone</string>
    <!-- Connected devices settings. Summary for hearing device input routing control dialog. -->
    <string name="bluetooth_hearing_device_input_routing_dialog_summary">Choose a microphone for calls.</string>
    <!-- Connected devices settings. One of the input routing option. It will change input routing to hearing aid microphone. -->
    <string name="bluetooth_hearing_device_input_routing_hearing_device_option">Hearing aid microphone</string>
    <!-- Connected devices settings. One of the input routing option. It will change input routing to this phone's builtin microphone -->
    <string name="bluetooth_hearing_device_input_routing_builtin_option">This phone\'s microphone</string>
    <!-- Connected devices settings. Title of the preference to show the entrance of the audio output page. It can change different types of audio are played on phone or other bluetooth devices. [CHAR LIMIT=35] -->
    <string name="bluetooth_audio_routing_title">Audio output</string>
    <!-- Title for bluetooth audio routing page footer. [CHAR LIMIT=30] -->
+1 −1
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
        final List<AudioProductStrategy> supportedStrategies =
                mAudioRoutingHelper.getSupportedStrategies(audioAttributes);
        final AudioDeviceAttributes hearingDeviceAttributes =
                mAudioRoutingHelper.getMatchedHearingDeviceAttributes(hearingDevice);
                mAudioRoutingHelper.getMatchedHearingDeviceAttributesForOutput(hearingDevice);
        if (hearingDeviceAttributes == null) {
            if (DEBUG) {
                Log.d(TAG,
+9 −1
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ public class BluetoothDetailsHearingDeviceController extends BluetoothDetailsCon

    public static final int ORDER_HEARING_DEVICE_SETTINGS = 1;
    public static final int ORDER_HEARING_AIDS_PRESETS = 2;
    public static final int ORDER_HEARING_DEVICE_INPUT_ROUTING = 3;
    public static final int ORDER_AMBIENT_VOLUME = 4;
    static final String KEY_HEARING_DEVICE_GROUP = "hearing_device_group";

@@ -62,10 +63,12 @@ public class BluetoothDetailsHearingDeviceController extends BluetoothDetailsCon
    @VisibleForTesting
    void setSubControllers(
            BluetoothDetailsHearingDeviceSettingsController hearingDeviceSettingsController,
            BluetoothDetailsHearingAidsPresetsController presetsController) {
            BluetoothDetailsHearingAidsPresetsController presetsController,
            BluetoothDetailsHearingDeviceInputRoutingController inputRoutingController) {
        mControllers.clear();
        mControllers.add(hearingDeviceSettingsController);
        mControllers.add(presetsController);
        mControllers.add(inputRoutingController);
    }

    @Override
@@ -112,6 +115,11 @@ public class BluetoothDetailsHearingDeviceController extends BluetoothDetailsCon
            mControllers.add(new BluetoothDetailsAmbientVolumePreferenceController(mContext,
                    mManager, mFragment, mCachedDevice, mLifecycle));
        }
        if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
            mControllers.add(
                    new BluetoothDetailsHearingDeviceInputRoutingController(mContext, mFragment,
                            mCachedDevice, mLifecycle));
        }
    }

    @NonNull
+126 −0
Original line number Diff line number Diff line
/*
 * 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.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP;
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.ORDER_HEARING_DEVICE_INPUT_ROUTING;

import android.content.Context;
import android.media.AudioManager;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.bluetooth.HearingDeviceInputRoutingPreference.InputRoutingValue;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants;
import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper;
import com.android.settingslib.core.lifecycle.Lifecycle;

import java.util.Arrays;

/**
 * The controller of the hearing device input routing
 *
 * <p> It manages the input routing preference and update the routing according to the value.
 */
public class BluetoothDetailsHearingDeviceInputRoutingController extends
        BluetoothDetailsController implements
        HearingDeviceInputRoutingPreference.InputRoutingCallback {

    private static final String TAG = "BluetoothDetailsHearingDeviceInputRoutingController";
    static final String KEY_HEARING_DEVICE_INPUT_ROUTING = "hearing_device_input_routing";

    private final HearingAidAudioRoutingHelper mAudioRoutingHelper;
    private final AudioManager mAudioManager;

    public BluetoothDetailsHearingDeviceInputRoutingController(
            @NonNull Context context,
            @NonNull PreferenceFragmentCompat fragment,
            @NonNull CachedBluetoothDevice device,
            @NonNull Lifecycle lifecycle) {
        super(context, fragment, device, lifecycle);
        mAudioRoutingHelper = new HearingAidAudioRoutingHelper(context);
        mAudioManager = mContext.getSystemService(AudioManager.class);
    }

    @Override
    public boolean isAvailable() {
        boolean isSupportedProfile = mCachedDevice.getProfiles().stream().anyMatch(
                profile -> profile instanceof HapClientProfile);
        boolean isSupportedInputDevice = Arrays.stream(
                mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).anyMatch(
                    info -> mCachedDevice.getAddress().equals(info.getAddress()));
        if (isSupportedProfile && !isSupportedInputDevice) {
            Log.d(TAG, "Not supported input type hearing device.");
        }
        return isSupportedProfile && isSupportedInputDevice;
    }

    @Override
    protected void init(PreferenceScreen screen) {
        PreferenceCategory hearingCategory = screen.findPreference(KEY_HEARING_DEVICE_GROUP);
        if (hearingCategory != null) {
            hearingCategory.addPreference(
                    createInputRoutingPreference(hearingCategory.getContext()));
        }
    }

    @Override
    protected void refresh() {}

    @Nullable
    @Override
    public String getPreferenceKey() {
        return KEY_HEARING_DEVICE_INPUT_ROUTING;
    }

    private HearingDeviceInputRoutingPreference createInputRoutingPreference(Context context) {
        HearingDeviceInputRoutingPreference pref = new HearingDeviceInputRoutingPreference(context);
        pref.setKey(KEY_HEARING_DEVICE_INPUT_ROUTING);
        pref.setOrder(ORDER_HEARING_DEVICE_INPUT_ROUTING);
        pref.setTitle(context.getString(R.string.bluetooth_hearing_device_input_routing_title));
        pref.setChecked(getUserPreferredInputRoutingValue());
        pref.setInputRoutingCallback(this);
        return pref;
    }

    @InputRoutingValue
    private int getUserPreferredInputRoutingValue() {
        return mCachedDevice.getDevice().isMicrophonePreferredForCalls()
                ? InputRoutingValue.HEARING_DEVICE : InputRoutingValue.BUILTIN_MIC;
    }

    @Override
    public void onInputRoutingUpdated(int selectedInputRoutingUiValue) {
        boolean useBuiltinMic =
                (selectedInputRoutingUiValue == InputRoutingValue.BUILTIN_MIC);
        boolean status = mAudioRoutingHelper.setPreferredInputDeviceForCalls(mCachedDevice,
                useBuiltinMic ? HearingAidAudioRoutingConstants.RoutingValue.BUILTIN_DEVICE
                        : HearingAidAudioRoutingConstants.RoutingValue.AUTO);
        if (!status) {
            Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls");
        }
        mCachedDevice.getDevice().setMicrophonePreferredForCalls(!useBuiltinMic);
    }
}
Loading