1 /* 2 * Copyright (C) 2024 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.systemui.car.ndo; 18 19 import android.content.Context; 20 import android.media.session.MediaController; 21 import android.media.session.MediaSessionManager; 22 import android.media.session.PlaybackState; 23 import android.os.UserHandle; 24 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.lifecycle.LiveData; 28 import androidx.lifecycle.MutableLiveData; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.concurrent.Executor; 33 34 /** 35 * Class that handles listening to and returning active media sessions. 36 */ 37 public class MediaSessionHelper extends MediaController.Callback { 38 39 private final MutableLiveData<List<MediaController>> mLiveData = new MutableLiveData<>(); 40 private final MediaSessionManager mMediaSessionManager; 41 private final UserHandle mUserHandle; 42 @VisibleForTesting 43 final List<MediaController> mMediaControllersList = new ArrayList<>(); 44 private final Executor mExecutor; 45 46 private final MediaSessionManager.OnActiveSessionsChangedListener mChangedListener = 47 this::onMediaSessionChange; 48 MediaSessionHelper(Context context, UserHandle userHandle)49 public MediaSessionHelper(Context context, UserHandle userHandle) { 50 mMediaSessionManager = context.getSystemService(MediaSessionManager.class); 51 mUserHandle = userHandle; 52 mExecutor = context.getMainExecutor(); 53 init(); 54 } 55 init()56 private void init() { 57 // Set initial data 58 onMediaSessionChange(mMediaSessionManager 59 .getActiveSessionsForUser(/* notificationListener= */ null, mUserHandle)); 60 61 mMediaSessionManager.addOnActiveSessionsChangedListener(/* notificationListener= */ null, 62 mUserHandle, mExecutor, mChangedListener); 63 } 64 65 /** Returns MediaControllers of current active media sessions */ getActiveMediaSessions()66 public LiveData<List<MediaController>> getActiveMediaSessions() { 67 return mLiveData; 68 } 69 70 /** Performs cleanup when MediaSessionHelper should no longer be used. */ cleanup()71 public void cleanup() { 72 mMediaSessionManager.removeOnActiveSessionsChangedListener(mChangedListener); 73 unregisterPlaybackChanges(); 74 } 75 76 @Override onPlaybackStateChanged(@ullable PlaybackState state)77 public void onPlaybackStateChanged(@Nullable PlaybackState state) { 78 if (isPausedOrActive(state)) { 79 onMediaSessionChange(mMediaSessionManager 80 .getActiveSessionsForUser(/* notificationListener= */ null, mUserHandle)); 81 } 82 } 83 onMediaSessionChange(List<MediaController> mediaControllers)84 private void onMediaSessionChange(List<MediaController> mediaControllers) { 85 unregisterPlaybackChanges(); 86 if (mediaControllers == null || mediaControllers.isEmpty()) { 87 return; 88 } 89 90 List<MediaController> activeMediaControllers = new ArrayList<>(); 91 92 for (MediaController mediaController : mediaControllers) { 93 if (isPausedOrActive(mediaController.getPlaybackState())) { 94 activeMediaControllers.add(mediaController); 95 } else { 96 // Since playback state changes don't trigger an active media session change, we 97 // need to listen to the other media sessions in case another one becomes active. 98 registerForPlaybackChanges(mediaController); 99 } 100 } 101 mLiveData.setValue(activeMediaControllers); 102 } 103 104 /** Returns whether the MediaController is paused active */ isPausedOrActive(PlaybackState playbackState)105 private boolean isPausedOrActive(PlaybackState playbackState) { 106 if (playbackState == null) { 107 return false; 108 } 109 return playbackState.isActive() || playbackState.getState() == PlaybackState.STATE_PAUSED; 110 } 111 registerForPlaybackChanges(MediaController controller)112 private void registerForPlaybackChanges(MediaController controller) { 113 if (mMediaControllersList.contains(controller)) { 114 return; 115 } 116 117 controller.registerCallback(this); 118 mMediaControllersList.add(controller); 119 } 120 unregisterPlaybackChanges()121 private void unregisterPlaybackChanges() { 122 for (MediaController mediaController : mMediaControllersList) { 123 if (mediaController != null) { 124 mediaController.unregisterCallback(this); 125 } 126 } 127 mMediaControllersList.clear(); 128 } 129 } 130