1 /*
2  * Copyright (C) 2018 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.sound;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.media.AudioManager;
23 import android.media.session.MediaController;
24 import android.media.session.MediaSessionManager;
25 import android.media.session.PlaybackState;
26 import android.text.TextUtils;
27 
28 import androidx.annotation.Nullable;
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.R;
33 import com.android.settingslib.Utils;
34 import com.android.settingslib.bluetooth.A2dpProfile;
35 import com.android.settingslib.bluetooth.HearingAidProfile;
36 import com.android.settingslib.media.MediaOutputSliceConstants;
37 
38 import java.util.List;
39 
40 /**
41  * This class allows launching MediaOutputSlice to switch output device.
42  * Preference would hide only when
43  * - Bluetooth = OFF
44  * - Bluetooth = ON and Connected Devices = 0 and Previously Connected = 0
45  * - Media stream captured by remote device
46  * - During a call.
47  */
48 public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {
49 
50     private MediaController mMediaController;
51 
MediaOutputPreferenceController(Context context, String key)52     public MediaOutputPreferenceController(Context context, String key) {
53         super(context, key);
54         mMediaController = getActiveLocalMediaController();
55     }
56 
57     @Override
displayPreference(PreferenceScreen screen)58     public void displayPreference(PreferenceScreen screen) {
59         super.displayPreference(screen);
60 
61         if (!Utils.isAudioModeOngoingCall(mContext) && mMediaController != null) {
62             mPreference.setVisible(true);
63         }
64     }
65 
66     @Override
updateState(Preference preference)67     public void updateState(Preference preference) {
68         if (preference == null) {
69             // In case UI is not ready.
70             return;
71         }
72 
73         if (mMediaController == null) {
74             // No active local playback
75             return;
76         }
77 
78         if (Utils.isAudioModeOngoingCall(mContext)) {
79             // Ongoing call status, switch entry for media will be disabled.
80             mPreference.setVisible(false);
81             preference.setSummary(
82                     mContext.getText(R.string.media_out_summary_ongoing_call_state));
83             return;
84         }
85 
86         BluetoothDevice activeDevice = null;
87         // Show preference if there is connected or previously connected device
88         // Find active device and set its name as the preference's summary
89         List<BluetoothDevice> connectedA2dpDevices = getConnectedA2dpDevices();
90         List<BluetoothDevice> connectedHADevices = getConnectedHearingAidDevices();
91         if (mAudioManager.getMode() == AudioManager.MODE_NORMAL
92                 && ((connectedA2dpDevices != null && !connectedA2dpDevices.isEmpty())
93                 || (connectedHADevices != null && !connectedHADevices.isEmpty()))) {
94             activeDevice = findActiveDevice();
95         }
96         mPreference.setTitle(mContext.getString(R.string.media_output_label_title,
97                 com.android.settings.Utils.getApplicationLabel(mContext,
98                         mMediaController.getPackageName())));
99         mPreference.setSummary((activeDevice == null) ?
100                 mContext.getText(R.string.media_output_default_summary) :
101                 activeDevice.getAlias());
102     }
103 
104     @Override
findActiveDevice()105     public BluetoothDevice findActiveDevice() {
106         BluetoothDevice activeDevice = findActiveHearingAidDevice();
107         final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
108 
109         if (activeDevice == null && a2dpProfile != null) {
110             activeDevice = a2dpProfile.getActiveDevice();
111         }
112         return activeDevice;
113     }
114 
115     /**
116      * Find active hearing aid device
117      */
118     @Override
findActiveHearingAidDevice()119     protected BluetoothDevice findActiveHearingAidDevice() {
120         final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
121 
122         if (hearingAidProfile != null) {
123             List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
124             for (BluetoothDevice btDevice : activeDevices) {
125                 if (btDevice != null) {
126                     return btDevice;
127                 }
128             }
129         }
130         return null;
131     }
132 
133     @Override
handlePreferenceTreeClick(Preference preference)134     public boolean handlePreferenceTreeClick(Preference preference) {
135         if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
136             final Intent intent = new Intent()
137                     .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
138                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
139             mContext.startActivity(intent);
140             return true;
141         }
142         return false;
143     }
144 
145     @Nullable
getActiveLocalMediaController()146     MediaController getActiveLocalMediaController() {
147         final MediaSessionManager mMediaSessionManager = mContext.getSystemService(
148                 MediaSessionManager.class);
149 
150         for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
151             final MediaController.PlaybackInfo pi = controller.getPlaybackInfo();
152             if (pi == null) {
153                 return null;
154             }
155             final PlaybackState playbackState = controller.getPlaybackState();
156             if (playbackState == null) {
157                 return null;
158             }
159             if (pi.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
160                     && playbackState.getState() == PlaybackState.STATE_PLAYING) {
161                 return controller;
162             }
163         }
164         return null;
165     }
166 }
167