Merge "[HA Input] Add UI to support hearing device microphone change ability in device details page" into main
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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 android.content.DialogInterface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.CustomDialogPreferenceCompat;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Preference for controlling the input routing for hearing device.
|
||||
*
|
||||
* <p> This preference displays a dialog that allows users to choose which input device that want to
|
||||
* use when using this hearing device.
|
||||
*/
|
||||
public class HearingDeviceInputRoutingPreference extends CustomDialogPreferenceCompat {
|
||||
|
||||
/**
|
||||
* Annotations for possible input routing UI for this hearing device input routing preference.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
InputRoutingValue.HEARING_DEVICE,
|
||||
InputRoutingValue.BUILTIN_MIC
|
||||
})
|
||||
public @interface InputRoutingValue {
|
||||
int HEARING_DEVICE = 0;
|
||||
int BUILTIN_MIC = 1;
|
||||
}
|
||||
|
||||
private static final int INVALID_ID = -1;
|
||||
private final Context mContext;
|
||||
private final int mFromHearingDeviceButtonId = R.id.input_from_hearing_device;
|
||||
private final int mFromBuiltinMicButtonId = R.id.input_from_builtin_mic;
|
||||
|
||||
@Nullable
|
||||
private RadioGroup mInputRoutingGroup;
|
||||
@Nullable
|
||||
private InputRoutingCallback mCallback;
|
||||
// Default value is hearing device as input
|
||||
@InputRoutingValue
|
||||
private int mSelectedInputRoutingValue = InputRoutingValue.HEARING_DEVICE;
|
||||
|
||||
|
||||
public HearingDeviceInputRoutingPreference(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public HearingDeviceInputRoutingPreference(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mContext = context;
|
||||
setDialogTitle(R.string.bluetooth_hearing_device_input_routing_dialog_title);
|
||||
setDialogLayoutResource(R.layout.hearing_device_input_routing_dialog);
|
||||
setNegativeButtonText(R.string.cancel);
|
||||
setPositiveButtonText(R.string.done_button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback to receive input routing updates.
|
||||
*/
|
||||
public void setInputRoutingCallback(@NonNull InputRoutingCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link InputRoutingValue} value to determine which radio button should be checked,
|
||||
* and also update summary accordingly.
|
||||
*
|
||||
* @param inputRoutingValue The input routing value.
|
||||
*/
|
||||
public void setChecked(@InputRoutingValue int inputRoutingValue) {
|
||||
mSelectedInputRoutingValue = inputRoutingValue;
|
||||
setSummary(getSummary());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick(DialogInterface dialog, int which) {
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
int prevBtnId = getRadioButtonId(mSelectedInputRoutingValue);
|
||||
int curBtnId = Objects.requireNonNull(mInputRoutingGroup).getCheckedRadioButtonId();
|
||||
if (prevBtnId == curBtnId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setChecked(getSelectedInputRoutingValue());
|
||||
if (mCallback != null) {
|
||||
mCallback.onInputRoutingUpdated(mSelectedInputRoutingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindDialogView(View view) {
|
||||
super.onBindDialogView(view);
|
||||
|
||||
mInputRoutingGroup = view.requireViewById(R.id.input_routing_group);
|
||||
mInputRoutingGroup.check(getRadioButtonId(mSelectedInputRoutingValue));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return switch (mSelectedInputRoutingValue) {
|
||||
case InputRoutingValue.HEARING_DEVICE -> mContext.getResources().getString(
|
||||
R.string.bluetooth_hearing_device_input_routing_hearing_device_option);
|
||||
case InputRoutingValue.BUILTIN_MIC -> mContext.getResources().getString(
|
||||
R.string.bluetooth_hearing_device_input_routing_builtin_option);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private int getRadioButtonId(@InputRoutingValue int inputRoutingValue) {
|
||||
return switch (inputRoutingValue) {
|
||||
case InputRoutingValue.HEARING_DEVICE -> mFromHearingDeviceButtonId;
|
||||
case InputRoutingValue.BUILTIN_MIC -> mFromBuiltinMicButtonId;
|
||||
default -> INVALID_ID;
|
||||
};
|
||||
}
|
||||
|
||||
@InputRoutingValue
|
||||
private int getSelectedInputRoutingValue() {
|
||||
int checkedId = Objects.requireNonNull(mInputRoutingGroup).getCheckedRadioButtonId();
|
||||
if (checkedId == mFromBuiltinMicButtonId) {
|
||||
return InputRoutingValue.BUILTIN_MIC;
|
||||
} else {
|
||||
// Should always return default value hearing device as input if something error
|
||||
// happens.
|
||||
return InputRoutingValue.HEARING_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when input routing changes.
|
||||
*/
|
||||
public interface InputRoutingCallback {
|
||||
|
||||
/**
|
||||
* Called when the positive button is clicked and input routing is changed.
|
||||
*
|
||||
* @param selectedInputRoutingValue The selected input routing value.
|
||||
*/
|
||||
void onInputRoutingUpdated(@InputRoutingValue int selectedInputRoutingValue);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user