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