/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settingslib.media; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; import android.media.RouteListingPreference; import android.media.RoutingSessionInfo; import android.media.session.MediaController; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.bluetooth.LocalBluetoothManager; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Template implementation of {@link InfoMediaManager} using {@link MediaRouter2Manager}. */ public class ManagerInfoMediaManager extends InfoMediaManager { private static final String TAG = "ManagerInfoMediaManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting /* package */ final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback(); @VisibleForTesting /* package */ MediaRouter2Manager mRouterManager; boolean mIsScanning = false; private final Executor mExecutor = Executors.newSingleThreadExecutor(); /* package */ ManagerInfoMediaManager( Context context, @NonNull String packageName, @NonNull UserHandle userHandle, LocalBluetoothManager localBluetoothManager, @Nullable MediaController mediaController) { super(context, packageName, userHandle, localBluetoothManager, mediaController); mRouterManager = MediaRouter2Manager.getInstance(context); } @Override protected void startScanOnRouter() { if (!mIsScanning) { mRouterManager.registerScanRequest(); mIsScanning = true; } } @Override protected void registerRouter() { mRouterManager.registerCallback(mExecutor, mMediaRouterCallback); } @Override protected void stopScanOnRouter() { if (mIsScanning) { mRouterManager.unregisterScanRequest(); mIsScanning = false; } } @Override protected void unregisterRouter() { mRouterManager.unregisterCallback(mMediaRouterCallback); } @Override protected void transferToRoute(@NonNull MediaRoute2Info route) { mRouterManager.transfer(mPackageName, route, mUserHandle); } @Override protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) { mRouterManager.selectRoute(info, route); } @Override protected void deselectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) { mRouterManager.deselectRoute(info, route); } @Override protected void releaseSession(@NonNull RoutingSessionInfo sessionInfo) { mRouterManager.releaseSession(sessionInfo); } @Override @NonNull protected List getSelectableRoutes(@NonNull RoutingSessionInfo info) { return mRouterManager.getSelectableRoutes(info); } @Override @NonNull protected List getDeselectableRoutes(@NonNull RoutingSessionInfo info) { return mRouterManager.getDeselectableRoutes(info); } @Override @NonNull protected List getSelectedRoutes(@NonNull RoutingSessionInfo info) { return mRouterManager.getSelectedRoutes(info); } @Override protected void setSessionVolume(@NonNull RoutingSessionInfo info, int volume) { mRouterManager.setSessionVolume(info, volume); } @Override protected void setRouteVolume(@NonNull MediaRoute2Info route, int volume) { mRouterManager.setRouteVolume(route, volume); } @Override @Nullable protected RouteListingPreference getRouteListingPreference() { return mRouterManager.getRouteListingPreference(mPackageName); } @Override @NonNull protected List getRoutingSessionsForPackage() { return mRouterManager.getRoutingSessions(mPackageName); } @Override @NonNull protected List getRemoteSessions() { return mRouterManager.getRemoteSessions(); } @Nullable @Override protected RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId) { for (RoutingSessionInfo sessionInfo : getRemoteSessions()) { if (TextUtils.equals(sessionInfo.getId(), sessionId)) { return sessionInfo; } } RoutingSessionInfo systemSession = mRouterManager.getSystemRoutingSession(null); return TextUtils.equals(systemSession.getId(), sessionId) ? systemSession : null; } @Override @NonNull protected List getAvailableRoutesFromRouter() { return mRouterManager.getAvailableRoutes(mPackageName); } @Override @NonNull protected List getTransferableRoutes(@NonNull String packageName) { return mRouterManager.getTransferableRoutes(packageName); } @VisibleForTesting /* package */ final class RouterManagerCallback implements MediaRouter2Manager.Callback { @Override public void onRoutesUpdated() { refreshDevices(); } @Override public void onPreferredFeaturesChanged(String packageName, List preferredFeatures) { if (TextUtils.equals(mPackageName, packageName)) { refreshDevices(); } } @Override public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) { if (DEBUG) { Log.d( TAG, "onTransferred() oldSession : " + oldSession.getName() + ", newSession : " + newSession.getName()); } rebuildDeviceList(); notifyCurrentConnectedDeviceChanged(); } /** * Ignore callback here since we'll also receive{@link * MediaRouter2Manager.Callback#onRequestFailed onRequestFailed} with reason code. */ @Override public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {} @Override public void onRequestFailed(int reason) { dispatchOnRequestFailed(reason); } @Override public void onSessionUpdated(RoutingSessionInfo sessionInfo) { refreshDevices(); } @Override public void onSessionReleased(@NonNull RoutingSessionInfo session) { refreshDevices(); } @Override public void onRouteListingPreferenceUpdated( String packageName, RouteListingPreference routeListingPreference) { if (!TextUtils.equals(mPackageName, packageName)) { return; } notifyRouteListingPreferenceUpdated(routeListingPreference); refreshDevices(); } } }