1 /* 2 * Copyright (C) 2020 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.notification; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.media.RoutingSessionInfo; 22 import android.text.TextUtils; 23 24 import androidx.annotation.VisibleForTesting; 25 import androidx.preference.Preference; 26 import androidx.preference.PreferenceCategory; 27 import androidx.preference.PreferenceScreen; 28 29 import com.android.settings.R; 30 import com.android.settings.Utils; 31 import com.android.settings.core.BasePreferenceController; 32 import com.android.settingslib.core.lifecycle.LifecycleObserver; 33 import com.android.settingslib.core.lifecycle.events.OnDestroy; 34 import com.android.settingslib.media.LocalMediaManager; 35 import com.android.settingslib.media.MediaDevice; 36 import com.android.settingslib.media.MediaOutputSliceConstants; 37 import com.android.settingslib.utils.ThreadUtils; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 /** 43 * A group preference controller to add/remove/update preference 44 * {@link com.android.settings.notification.RemoteVolumeSeekBarPreference} 45 **/ 46 public class RemoteVolumeGroupController extends BasePreferenceController implements 47 Preference.OnPreferenceChangeListener, LifecycleObserver, OnDestroy, 48 LocalMediaManager.DeviceCallback { 49 50 private static final String KEY_REMOTE_VOLUME_GROUP = "remote_media_group"; 51 private static final String TAG = "RemoteVolumePrefCtr"; 52 @VisibleForTesting 53 static final String SWITCHER_PREFIX = "OUTPUT_SWITCHER"; 54 55 private PreferenceCategory mPreferenceCategory; 56 private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); 57 58 @VisibleForTesting 59 LocalMediaManager mLocalMediaManager; 60 RemoteVolumeGroupController(Context context, String preferenceKey)61 public RemoteVolumeGroupController(Context context, String preferenceKey) { 62 super(context, preferenceKey); 63 if (mLocalMediaManager == null) { 64 mLocalMediaManager = new LocalMediaManager(mContext, null, null); 65 mLocalMediaManager.registerCallback(this); 66 mLocalMediaManager.startScan(); 67 } 68 } 69 70 @Override getAvailabilityStatus()71 public int getAvailabilityStatus() { 72 if (mRoutingSessionInfos.isEmpty()) { 73 return CONDITIONALLY_UNAVAILABLE; 74 } 75 return AVAILABLE_UNSEARCHABLE; 76 } 77 78 @Override displayPreference(PreferenceScreen screen)79 public void displayPreference(PreferenceScreen screen) { 80 super.displayPreference(screen); 81 mPreferenceCategory = screen.findPreference(getPreferenceKey()); 82 initRemoteMediaSession(); 83 refreshPreference(); 84 } 85 initRemoteMediaSession()86 private void initRemoteMediaSession() { 87 mRoutingSessionInfos.clear(); 88 for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) { 89 if (!info.isSystemSession()) { 90 mRoutingSessionInfos.add(info); 91 } 92 } 93 } 94 95 @Override onDestroy()96 public void onDestroy() { 97 mLocalMediaManager.unregisterCallback(this); 98 mLocalMediaManager.stopScan(); 99 } 100 refreshPreference()101 private void refreshPreference() { 102 mPreferenceCategory.removeAll(); 103 if (!isAvailable()) { 104 mPreferenceCategory.setVisible(false); 105 return; 106 } 107 final CharSequence castVolume = mContext.getText(R.string.remote_media_volume_option_title); 108 mPreferenceCategory.setVisible(true); 109 110 for (RoutingSessionInfo info : mRoutingSessionInfos) { 111 if (mPreferenceCategory.findPreference(info.getId()) != null) { 112 continue; 113 } 114 final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title, 115 Utils.getApplicationLabel(mContext, info.getClientPackageName())); 116 // Add slider 117 final RemoteVolumeSeekBarPreference seekBarPreference = 118 new RemoteVolumeSeekBarPreference(mContext); 119 seekBarPreference.setKey(info.getId()); 120 seekBarPreference.setTitle(castVolume); 121 seekBarPreference.setMax(info.getVolumeMax()); 122 seekBarPreference.setProgress(info.getVolume()); 123 seekBarPreference.setMin(0); 124 seekBarPreference.setOnPreferenceChangeListener(this); 125 seekBarPreference.setIcon(R.drawable.ic_volume_remote); 126 mPreferenceCategory.addPreference(seekBarPreference); 127 // Add output indicator 128 final Preference preference = new Preference(mContext); 129 preference.setKey(SWITCHER_PREFIX + info.getId()); 130 preference.setTitle(outputTitle); 131 preference.setSummary(info.getName()); 132 mPreferenceCategory.addPreference(preference); 133 } 134 } 135 136 @Override onPreferenceChange(Preference preference, Object newValue)137 public boolean onPreferenceChange(Preference preference, Object newValue) { 138 ThreadUtils.postOnBackgroundThread(() -> { 139 mLocalMediaManager.adjustSessionVolume(preference.getKey(), (int) newValue); 140 }); 141 return true; 142 } 143 144 @Override handlePreferenceTreeClick(Preference preference)145 public boolean handlePreferenceTreeClick(Preference preference) { 146 if (!preference.getKey().startsWith(SWITCHER_PREFIX)) { 147 return false; 148 } 149 for (RoutingSessionInfo info : mRoutingSessionInfos) { 150 if (TextUtils.equals(info.getId(), 151 preference.getKey().substring(SWITCHER_PREFIX.length()))) { 152 final Intent intent = new Intent() 153 .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) 154 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 155 .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, 156 info.getClientPackageName()); 157 mContext.startActivity(intent); 158 return true; 159 } 160 } 161 return false; 162 } 163 164 @Override getPreferenceKey()165 public String getPreferenceKey() { 166 return KEY_REMOTE_VOLUME_GROUP; 167 } 168 169 @Override onDeviceListUpdate(List<MediaDevice> devices)170 public void onDeviceListUpdate(List<MediaDevice> devices) { 171 if (mPreferenceCategory == null) { 172 // Preference group is not ready. 173 return; 174 } 175 initRemoteMediaSession(); 176 refreshPreference(); 177 } 178 179 @Override onSelectedDeviceStateChanged(MediaDevice device, int state)180 public void onSelectedDeviceStateChanged(MediaDevice device, int state) { 181 } 182 } 183