1 /*
2  * Copyright (C) 2023 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.settings.accessibility;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.media.AudioAttributes;
22 import android.media.AudioDeviceAttributes;
23 import android.media.audiopolicy.AudioProductStrategy;
24 import android.util.Log;
25 
26 import androidx.preference.ListPreference;
27 import androidx.preference.Preference;
28 
29 import com.android.settings.core.BasePreferenceController;
30 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
31 import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants;
32 import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper;
33 
34 import com.google.common.annotations.VisibleForTesting;
35 import com.google.common.primitives.Ints;
36 
37 import java.util.List;
38 import java.util.stream.Collectors;
39 
40 /**
41  * Abstract class for providing audio routing {@link ListPreference} common control for hearing
42  * device specifically.
43  */
44 public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
45         BasePreferenceController implements Preference.OnPreferenceChangeListener {
46 
47     private static final String TAG = "HARoutingBasePreferenceController";
48     private static final boolean DEBUG = false;
49 
50     private final HearingAidAudioRoutingHelper mAudioRoutingHelper;
51     private final HearingAidHelper mHearingAidHelper;
52 
HearingDeviceAudioRoutingBasePreferenceController(Context context, String preferenceKey)53     public HearingDeviceAudioRoutingBasePreferenceController(Context context,
54             String preferenceKey) {
55         this(context, preferenceKey,
56                 new HearingAidAudioRoutingHelper(context),
57                 new HearingAidHelper(context));
58     }
59 
60     @VisibleForTesting
HearingDeviceAudioRoutingBasePreferenceController(Context context, String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper, HearingAidHelper hearingAidHelper)61     public HearingDeviceAudioRoutingBasePreferenceController(Context context,
62             String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper,
63             HearingAidHelper hearingAidHelper) {
64         super(context, preferenceKey);
65 
66         mAudioRoutingHelper = audioRoutingHelper;
67         mHearingAidHelper = hearingAidHelper;
68     }
69 
70     @Override
getAvailabilityStatus()71     public int getAvailabilityStatus() {
72         return AVAILABLE;
73     }
74 
75     @Override
updateState(Preference preference)76     public void updateState(Preference preference) {
77         super.updateState(preference);
78 
79         final ListPreference listPreference = (ListPreference) preference;
80         final int routingValue = restoreRoutingValue(mContext);
81         listPreference.setValue(String.valueOf(routingValue));
82     }
83 
84     @Override
onPreferenceChange(Preference preference, Object newValue)85     public boolean onPreferenceChange(Preference preference, Object newValue) {
86         final Integer routingValue = Ints.tryParse((String) newValue);
87 
88         saveRoutingValue(mContext, routingValue);
89         final CachedBluetoothDevice device = mHearingAidHelper.getConnectedHearingAidDevice();
90         if (device != null) {
91             trySetAudioRoutingConfig(getSupportedAttributeList(),
92                     mHearingAidHelper.getConnectedHearingAidDevice(), routingValue);
93         }
94 
95         return true;
96     }
97 
trySetAudioRoutingConfig(int[] audioAttributes, CachedBluetoothDevice hearingDevice, @HearingAidAudioRoutingConstants.RoutingValue int routingValue)98     private void trySetAudioRoutingConfig(int[] audioAttributes,
99             CachedBluetoothDevice hearingDevice,
100             @HearingAidAudioRoutingConstants.RoutingValue int routingValue) {
101         final List<AudioProductStrategy> supportedStrategies =
102                 mAudioRoutingHelper.getSupportedStrategies(audioAttributes);
103         final AudioDeviceAttributes hearingDeviceAttributes =
104                 mAudioRoutingHelper.getMatchedHearingDeviceAttributes(hearingDevice);
105         if (hearingDeviceAttributes == null) {
106             if (DEBUG) {
107                 Log.d(TAG,
108                         "Can not find expected AudioDeviceAttributes to config audio routing "
109                                 + "maybe device is offline: "
110                                 + hearingDevice.getDevice().getAnonymizedAddress());
111             }
112             return;
113         }
114 
115         final boolean status = mAudioRoutingHelper.setPreferredDeviceRoutingStrategies(
116                 supportedStrategies, hearingDeviceAttributes, routingValue);
117 
118         if (!status) {
119             final List<String> strategiesName = supportedStrategies.stream()
120                     .map(AudioProductStrategy::getName)
121                     .collect(Collectors.toList());
122             Log.w(TAG, "routingMode: " + strategiesName + " routingValue: " + routingValue
123                     + " fail to configure AudioProductStrategy");
124         }
125     }
126 
127     /**
128      * Gets a list of usage values defined in {@link AudioAttributes} that are used to identify
129      * {@link AudioProductStrategy} to configure audio routing.
130      */
getSupportedAttributeList()131     protected abstract int[] getSupportedAttributeList();
132 
133     /**
134      * Saves the routing value.
135      *
136      * @param context the valid context used to get the {@link ContentResolver}
137      * @param routingValue one of the value defined in
138      *                     {@link HearingAidAudioRoutingConstants.RoutingValue}
139      */
saveRoutingValue(Context context, int routingValue)140     protected abstract void saveRoutingValue(Context context, int routingValue);
141 
142     /**
143      * Restores the routing value and used to reflect status on ListPreference.
144      *
145      * @param context the valid context used to get the {@link ContentResolver}
146      * @return one of the value defined in {@link HearingAidAudioRoutingConstants.RoutingValue}
147      */
restoreRoutingValue(Context context)148     protected abstract int restoreRoutingValue(Context context);
149 }
150