1 /*
2  * Copyright (C) 2019 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.car.developeroptions.sound;
18 
19 import static android.media.AudioManager.STREAM_MUSIC;
20 import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
21 
22 import android.bluetooth.BluetoothDevice;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.media.AudioManager;
26 import android.text.TextUtils;
27 
28 import androidx.preference.Preference;
29 
30 import com.android.car.developeroptions.R;
31 import com.android.settingslib.Utils;
32 import com.android.settingslib.bluetooth.A2dpProfile;
33 import com.android.settingslib.bluetooth.HearingAidProfile;
34 import com.android.settingslib.media.MediaOutputSliceConstants;
35 
36 import java.util.List;
37 
38 /**
39  * This class allows launching MediaOutputSlice to switch output device.
40  * Preference would hide only when
41  * - Bluetooth = OFF
42  * - Bluetooth = ON and Connected Devices = 0 and Previously Connected = 0
43  * - Media stream captured by remote device
44  * - During a call.
45  */
46 public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {
47 
MediaOutputPreferenceController(Context context, String key)48     public MediaOutputPreferenceController(Context context, String key) {
49         super(context, key);
50     }
51 
52     @Override
updateState(Preference preference)53     public void updateState(Preference preference) {
54         if (preference == null) {
55             // In case UI is not ready.
56             return;
57         }
58 
59         if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_REMOTE_SUBMIX)) {
60             // In cast mode, disable switch entry.
61             mPreference.setVisible(false);
62             preference.setSummary(mContext.getText(R.string.media_output_summary_unavailable));
63             return;
64         }
65 
66         if (Utils.isAudioModeOngoingCall(mContext)) {
67             // Ongoing call status, switch entry for media will be disabled.
68             mPreference.setVisible(false);
69             preference.setSummary(
70                     mContext.getText(R.string.media_out_summary_ongoing_call_state));
71             return;
72         }
73 
74         boolean deviceConnectable = false;
75         BluetoothDevice activeDevice = null;
76         // Show preference if there is connected or previously connected device
77         // Find active device and set its name as the preference's summary
78         List<BluetoothDevice> connectableA2dpDevices = getConnectableA2dpDevices();
79         List<BluetoothDevice> connectableHADevices = getConnectableHearingAidDevices();
80         if (mAudioManager.getMode() == AudioManager.MODE_NORMAL
81                 && ((connectableA2dpDevices != null && !connectableA2dpDevices.isEmpty())
82                 || (connectableHADevices != null && !connectableHADevices.isEmpty()))) {
83             deviceConnectable = true;
84             activeDevice = findActiveDevice();
85         }
86         mPreference.setVisible(deviceConnectable);
87         mPreference.setSummary((activeDevice == null) ?
88                 mContext.getText(R.string.media_output_default_summary) :
89                 activeDevice.getAlias());
90     }
91 
92     @Override
findActiveDevice()93     public BluetoothDevice findActiveDevice() {
94         BluetoothDevice activeDevice = findActiveHearingAidDevice();
95         final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
96 
97         if (activeDevice == null && a2dpProfile != null) {
98             activeDevice = a2dpProfile.getActiveDevice();
99         }
100         return activeDevice;
101     }
102 
103     /**
104      * Find active hearing aid device
105      */
106     @Override
findActiveHearingAidDevice()107     protected BluetoothDevice findActiveHearingAidDevice() {
108         final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
109 
110         if (hearingAidProfile != null) {
111             List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
112             for (BluetoothDevice btDevice : activeDevices) {
113                 if (btDevice != null) {
114                     return btDevice;
115                 }
116             }
117         }
118         return null;
119     }
120 
121     @Override
handlePreferenceTreeClick(Preference preference)122     public boolean handlePreferenceTreeClick(Preference preference) {
123         if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
124             final Intent intent = new Intent()
125                     .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
126                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
127             mContext.startActivity(intent);
128             return true;
129         }
130         return false;
131     }
132 }
133