199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker/* 299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Copyright 2018 The Android Open Source Project 399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * 499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Licensed under the Apache License, Version 2.0 (the "License"); 599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * you may not use this file except in compliance with the License. 699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * You may obtain a copy of the License at 799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * 899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * http://www.apache.org/licenses/LICENSE-2.0 999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * 1099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Unless required by applicable law or agreed to in writing, software 1199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * distributed under the License is distributed on an "AS IS" BASIS, 1299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * See the License for the specific language governing permissions and 1499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * limitations under the License. 1599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker */ 1699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 1799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerpackage com.android.bluetooth.avrcp; 1899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 198b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panickerimport android.annotation.NonNull; 2099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.BroadcastReceiver; 2199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.ComponentName; 2299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.Context; 2399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.Intent; 2499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.IntentFilter; 2599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.pm.PackageManager; 2699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.pm.ResolveInfo; 2799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.MediaSession; 2899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.MediaSessionManager; 2999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.PlaybackState; 3099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.os.Handler; 3199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.os.Looper; 3299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.util.Log; 3399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.view.KeyEvent; 3499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 350f5b951d00b5efb447774eabb899872e6cf83ccbtedwangimport com.android.bluetooth.Utils; 360f5b951d00b5efb447774eabb899872e6cf83ccbtedwang 3799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.ArrayList; 3899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.Collections; 3999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.HashMap; 4099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.HashSet; 4199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.List; 4299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.Map; 4399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.regex.Matcher; 4499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.regex.Pattern; 4599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 4699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker/** 4799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * This class is directly responsible of maintaining the list of Browsable Players as well as 4899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * the list of Addressable Players. This variation of the list doesn't actually list all the 4999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * available players for a getAvailableMediaPlayers request. Instead it only reports one media 5099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * player with ID=0 and all the other browsable players are folders in the root of that player. 5199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * 5299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Changing the directory to a browsable player will allow you to traverse that player as normal. 5399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * By only having one root player, we never have to send Addressed Player Changed notifications, 5499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * UIDs Changed notifications, or Available Players Changed notifications. 5599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * 5699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * TODO (apanicke): Add non-browsable players as song items to the root folder. Selecting that 5799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * player would effectively cause player switch by sending a play command to that player. 5899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker */ 5999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerpublic class MediaPlayerList { 6099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final String TAG = "NewAvrcpMediaPlayerList"; 6199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final boolean DEBUG = true; 6299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker static boolean sTesting = false; 6399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 6499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final String PACKAGE_SCHEME = "package"; 6599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final int NO_ACTIVE_PLAYER = 0; 6699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final int BLUETOOTH_PLAYER_ID = 0; 6799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final String BLUETOOTH_PLAYER_NAME = "Bluetooth Player"; 6899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 6999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // mediaId's for the now playing list will be in the form of "NowPlayingId[XX]" where [XX] 7099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // is the Queue ID for the requested item. 7199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final String NOW_PLAYING_ID_PATTERN = Util.NOW_PLAYING_PREFIX + "([0-9]*)"; 7299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 7399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // mediaId's for folder browsing will be in the form of [XX][mediaid], where [XX] is a 7499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // two digit representation of the player id and [mediaid] is the original media id as a 7599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // string. 7699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static final String BROWSE_ID_PATTERN = "\\d\\d.*"; 7799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 7899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private Context mContext; 7999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private Looper mLooper; // Thread all media player callbacks and timeouts happen on 8099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private PackageManager mPackageManager; 8199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private MediaSessionManager mMediaSessionManager; 8299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 8399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private Map<Integer, MediaPlayerWrapper> mMediaPlayers = 8499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>()); 8599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private Map<String, Integer> mMediaPlayerIds = 8699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Collections.synchronizedMap(new HashMap<String, Integer>()); 8799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private Map<Integer, BrowsedPlayerWrapper> mBrowsablePlayers = 8899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>()); 8999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private int mActivePlayerId = NO_ACTIVE_PLAYER; 9099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 910f5b951d00b5efb447774eabb899872e6cf83ccbtedwang private AvrcpTargetService.ListCallback mCallback; 9299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 9399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker interface MediaUpdateCallback { 9499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void run(MediaData data); 9599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 9699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 9799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker interface GetPlayerRootCallback { 9899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void run(int playerId, boolean success, String rootId, int numItems); 9999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 10099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 10199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker interface GetFolderItemsCallback { 10299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void run(String parentId, List<ListItem> items); 10399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 10499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 1050f5b951d00b5efb447774eabb899872e6cf83ccbtedwang interface FolderUpdateCallback { 1060f5b951d00b5efb447774eabb899872e6cf83ccbtedwang void run(boolean availablePlayers, boolean addressedPlayers, boolean uids); 1070f5b951d00b5efb447774eabb899872e6cf83ccbtedwang } 1080f5b951d00b5efb447774eabb899872e6cf83ccbtedwang 109fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker MediaPlayerList(Looper looper, Context context) { 110fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker Log.v(TAG, "Creating MediaPlayerList"); 111fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker 11299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mLooper = looper; 11399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mContext = context; 11499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 11599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Register for intents where available players might have changed 11699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker IntentFilter pkgFilter = new IntentFilter(); 11799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 11899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 11999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 12099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 12199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker pkgFilter.addDataScheme(PACKAGE_SCHEME); 12299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter); 12399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 12499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager = 12599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); 12699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.addOnActiveSessionsChangedListener( 12799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mActiveSessionsChangedListener, null, new Handler(looper)); 12899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.setCallback(mButtonDispatchCallback, null); 129fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker } 130fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker 1310f5b951d00b5efb447774eabb899872e6cf83ccbtedwang void init(AvrcpTargetService.ListCallback callback) { 132fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker Log.v(TAG, "Initializing MediaPlayerList"); 133fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker mCallback = callback; 13499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 13599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Build the list of browsable players and afterwards, build the list of media players 13699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE); 13799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<ResolveInfo> playerList = 13899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mContext 13999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker .getApplicationContext() 14099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker .getPackageManager() 14199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker .queryIntentServices(intent, PackageManager.MATCH_ALL); 14299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 14399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker BrowsablePlayerConnector.connectToPlayers(mContext, mLooper, playerList, 14499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker (List<BrowsedPlayerWrapper> players) -> { 14599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "init: Browsable Player list size is " + players.size()); 14699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 147084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker // Check to see if the list has been cleaned up before this completed 148084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker if (mMediaSessionManager == null) { 149084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker return; 150084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker } 151084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker 15299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (BrowsedPlayerWrapper wrapper : players) { 15399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Generate new id and add the browsable player 15499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayerIds.containsKey(wrapper.getPackageName())) { 15599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayerIds.put(wrapper.getPackageName(), mMediaPlayerIds.size() + 1); 15699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 15799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 15899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("Adding Browser Wrapper for " + wrapper.getPackageName() + " with id " 15999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + mMediaPlayerIds.get(wrapper.getPackageName())); 16099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 16199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mBrowsablePlayers.put(mMediaPlayerIds.get(wrapper.getPackageName()), wrapper); 16299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 16399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker wrapper.getFolderItems(wrapper.getRootId(), 16499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker (int status, String mediaId, List<ListItem> results) -> { 16599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("Got the contents for: " + mediaId + " : num results=" 16699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + results.size()); 16799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }); 16899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 16999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 17099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Construct the list of current players 17199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("Initializing list of current media players"); 17299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<android.media.session.MediaController> controllers = 17399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.getActiveSessions(null); 17499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 17599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (android.media.session.MediaController controller : controllers) { 17699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker addMediaPlayer(controller); 17799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 17899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 17999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // If there were any active players and we don't already have one due to the Media 18099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Framework Callbacks then set the highest priority one to active 18199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mActivePlayerId == 0 && mMediaPlayers.size() > 0) setActivePlayer(1); 18299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }); 18399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 18499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 18599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void cleanup() { 18699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mContext.unregisterReceiver(mPackageChangedBroadcastReceiver); 18799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 18899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveSessionsChangedListener); 18999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.setCallback(null, null); 19099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager = null; 19199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 19299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayerIds.clear(); 19399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 19499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (MediaPlayerWrapper player : mMediaPlayers.values()) { 19599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker player.cleanup(); 19699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 19799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayers.clear(); 19899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 19999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) { 20099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker player.disconnect(); 20199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 20299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mBrowsablePlayers.clear(); 20399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 20499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 20599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int getCurrentPlayerId() { 20699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return BLUETOOTH_PLAYER_ID; 20799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 20899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 20999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker MediaPlayerWrapper getActivePlayer() { 21099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return mMediaPlayers.get(mActivePlayerId); 21199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 21299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 21399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 21499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 21599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // In this case the displayed player is the Bluetooth Player, the number of items is equal 21699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // to the number of players. The root ID will always be empty string in this case as well. 21799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void getPlayerRoot(int playerId, GetPlayerRootCallback cb) { 218f499c3d8ca788862ed618f659b152b0bfe7bef47Hansong Zhang cb.run(playerId, playerId == BLUETOOTH_PLAYER_ID, "", mBrowsablePlayers.size()); 21999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 22099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 22199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Return the "Bluetooth Player" as the only player always 22299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<PlayerInfo> getMediaPlayerList() { 22399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker PlayerInfo info = new PlayerInfo(); 22499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker info.id = BLUETOOTH_PLAYER_ID; 22599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker info.name = BLUETOOTH_PLAYER_NAME; 22699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker info.browsable = true; 22799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<PlayerInfo> ret = new ArrayList<PlayerInfo>(); 22899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker ret.add(info); 22999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 23099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return ret; 23199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 23299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 2338b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker @NonNull 23499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String getCurrentMediaId() { 23599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker final MediaPlayerWrapper player = getActivePlayer(); 23699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (player == null) return ""; 23799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 238eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker final PlaybackState state = player.getPlaybackState(); 2398b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker final List<Metadata> queue = player.getCurrentQueue(); 2408b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker 2418b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker // Disable the now playing list if the player doesn't have a queue or provide an active 2428b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker // queue ID that can be used to determine the active song in the queue. 2438b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker if (state == null 2448b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker || state.getActiveQueueItemId() == MediaSession.QueueItem.UNKNOWN_ID 2458b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker || queue.size() == 0) { 2468b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker d("getCurrentMediaId: No active queue item Id sending empty mediaId: PlaybackState=" 2478b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker + state); 24899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return ""; 24999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 25099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 25199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return Util.NOW_PLAYING_PREFIX + state.getActiveQueueItemId(); 25299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 25399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 2548b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker @NonNull 25599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Metadata getCurrentSongInfo() { 25699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker final MediaPlayerWrapper player = getActivePlayer(); 25799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (player == null) return Util.empty_data(); 25899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 259eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker return player.getCurrentMetadata(); 26099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 26199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 26299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker PlaybackState getCurrentPlayStatus() { 26399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker final MediaPlayerWrapper player = getActivePlayer(); 26499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (player == null) return null; 26599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 266eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker return player.getPlaybackState(); 26799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 26899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 2698b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker @NonNull 27099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<Metadata> getNowPlayingList() { 2718b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker // Only send the current song for the now playing if there is no active song. See 2728b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker // |getCurrentMediaId()| for reasons why there might be no active song. 2738b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker if (getCurrentMediaId().equals("")) { 27499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<Metadata> ret = new ArrayList<Metadata>(); 2758b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker Metadata data = getCurrentSongInfo(); 2768b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker data.mediaId = ""; 2778b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker ret.add(data); 27899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return ret; 27999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 28099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 281eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker return getActivePlayer().getCurrentQueue(); 28299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 28399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 28499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void playItem(int playerId, boolean nowPlaying, String mediaId) { 28599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (nowPlaying) { 28699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker playNowPlayingItem(mediaId); 28799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } else { 28899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker playFolderItem(mediaId); 28999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 29099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 29199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 29299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private void playNowPlayingItem(String mediaId) { 29399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("playNowPlayingItem: mediaId=" + mediaId); 29499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 29599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Pattern regex = Pattern.compile(NOW_PLAYING_ID_PATTERN); 29699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Matcher m = regex.matcher(mediaId); 29799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!m.find()) { 29899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // This should never happen since we control the media ID's reported 29999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.wtf(TAG, "playNowPlayingItem: Couldn't match mediaId to pattern: mediaId=" 30099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + mediaId); 30199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 30299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 30399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker long queueItemId = Long.parseLong(m.group(1)); 30499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (getActivePlayer() != null) { 30599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker getActivePlayer().playItemFromQueue(queueItemId); 30699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 30799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 30899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 30999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private void playFolderItem(String mediaId) { 31099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("playFolderItem: mediaId=" + mediaId); 31199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 31299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mediaId.matches(BROWSE_ID_PATTERN)) { 31399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // This should never happen since we control the media ID's reported 31499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.wtf(TAG, "playFolderItem: mediaId didn't match pattern: mediaId=" + mediaId); 31599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 31699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 31799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int playerIndex = Integer.parseInt(mediaId.substring(0, 2)); 31899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String itemId = mediaId.substring(2); 31999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 32099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mBrowsablePlayers.containsKey(playerIndex)) { 32199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker e("playFolderItem: Do not have the a browsable player with ID " + playerIndex); 32299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 32399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 32499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 32599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mBrowsablePlayers.get(playerIndex).playItem(itemId); 32699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 32799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 32899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void getFolderItemsMediaPlayerList(GetFolderItemsCallback cb) { 32999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("getFolderItemsMediaPlayerList: Sending Media Player list for root directory"); 33099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 33199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker ArrayList<ListItem> playerList = new ArrayList<ListItem>(); 33299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) { 33399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 33499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String displayName = Util.getDisplayName(mContext, player.getPackageName()); 33599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int id = mMediaPlayerIds.get(player.getPackageName()); 33699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 33799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("getFolderItemsMediaPlayerList: Adding player " + displayName); 33899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Folder playerFolder = new Folder(String.format("%02d", id), false, displayName); 33999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker playerList.add(new ListItem(playerFolder)); 34099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 34199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker cb.run("", playerList); 34299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 34399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 34499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 34599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void getFolderItems(int playerId, String mediaId, GetFolderItemsCallback cb) { 34699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // The playerId is unused since we always assume the remote device is using the 34799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Bluetooth Player. 34899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("getFolderItems(): playerId=" + playerId + ", mediaId=" + mediaId); 34999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 35099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // The device is requesting the content of the root folder. This folder contains a list of 35199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Browsable Media Players displayed as folders with their contents contained within. 35299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mediaId.equals("")) { 35399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker getFolderItemsMediaPlayerList(cb); 35499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 35599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 35699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 35799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mediaId.matches(BROWSE_ID_PATTERN)) { 35899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // This should never happen since we control the media ID's reported 35999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.wtf(TAG, "getFolderItems: mediaId didn't match pattern: mediaId=" + mediaId); 36099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 36199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 36299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int playerIndex = Integer.parseInt(mediaId.substring(0, 2)); 36399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String itemId = mediaId.substring(2); 36499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 36599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add timeouts for looking up folder items since media browsers don't 36699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // have to respond. 36799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mBrowsablePlayers.containsKey(playerIndex)) { 36899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker BrowsedPlayerWrapper wrapper = mBrowsablePlayers.get(playerIndex); 36999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (itemId.equals("")) { 37099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "Empty media id, getting the root for " 37199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + wrapper.getPackageName()); 37299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker itemId = wrapper.getRootId(); 37399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 37499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 37599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker wrapper.getFolderItems(itemId, (status, id, results) -> { 37699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (status != BrowsedPlayerWrapper.STATUS_SUCCESS) { 37799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker cb.run(mediaId, new ArrayList<ListItem>()); 37899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 37999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 38099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 38199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String playerPrefix = String.format("%02d", playerIndex); 38299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (ListItem item : results) { 38399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (item.isFolder) { 38499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker item.folder.mediaId = playerPrefix.concat(item.folder.mediaId); 38599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } else { 38699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker item.song.mediaId = playerPrefix.concat(item.song.mediaId); 38799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 38899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 38999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker cb.run(mediaId, results); 39099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }); 39199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 39299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } else { 39399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker cb.run(mediaId, new ArrayList<ListItem>()); 39499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 39599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 39699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 39799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Adds the controller to the MediaPlayerList or updates the controller if we already had 39899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // a controller for a package. Returns the new ID of the controller where its added or its 39999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // previous value if it already existed. Returns -1 if the controller passed in is invalid 40099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int addMediaPlayer(android.media.session.MediaController controller) { 40199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (controller == null) return -1; 40299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 40399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that 40499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // there is no active player. If we already have a browsable player for the package, reuse 40599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // that key. 40699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String packageName = controller.getPackageName(); 40799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayerIds.containsKey(packageName)) { 40899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayerIds.put(packageName, mMediaPlayerIds.size() + 1); 40999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 41099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 41199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker int playerId = mMediaPlayerIds.get(packageName); 41299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 41399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // If we already have a controller for the package, then update it with this new controller 41499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // as the old controller has probably gone stale. 41599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mMediaPlayers.containsKey(playerId)) { 41699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("Already have a controller for the player: " + packageName + ", updating instead"); 41799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker MediaPlayerWrapper player = mMediaPlayers.get(playerId); 41899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker player.updateMediaController(MediaControllerFactory.wrap(controller)); 41999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 42099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // If the media controller we updated was the active player check if the media updated 42199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (playerId == mActivePlayerId) { 42299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker sendMediaUpdate(getActivePlayer().getCurrentMediaData()); 42399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 42499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 42599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return playerId; 42699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 42799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 42899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap( 42999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker MediaControllerFactory.wrap(controller), 43099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mLooper); 43199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 43299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: " 43399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + mMediaPlayerIds.get(controller.getPackageName())); 43499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 43599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayers.put(playerId, newPlayer); 43699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return playerId; 43799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 43899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 43999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void removeMediaPlayer(int playerId) { 44099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayers.containsKey(playerId)) { 44199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker e("Trying to remove nonexistent media player: " + playerId); 44299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 44399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 44499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 44599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // If we removed the active player, set no player as active until the Media Framework 44699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // tells us otherwise 44799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (playerId == mActivePlayerId && playerId != NO_ACTIVE_PLAYER) { 44899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker getActivePlayer().unregisterCallback(); 44999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mActivePlayerId = NO_ACTIVE_PLAYER; 45099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 45199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 4529e18c9de74a1882e629ee99a037be46b3ec18bbaHansong Zhang final MediaPlayerWrapper wrapper = mMediaPlayers.get(playerId); 45399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("Removing media player " + wrapper.getPackageName()); 45499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayerIds.remove(wrapper.getPackageName()); 45599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaPlayers.remove(playerId); 4569e18c9de74a1882e629ee99a037be46b3ec18bbaHansong Zhang wrapper.cleanup(); 45799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 45899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 45999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void setActivePlayer(int playerId) { 46099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayers.containsKey(playerId)) { 46199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker e("Player doesn't exist in list(): " + playerId); 46299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 46399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 46499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 46599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (playerId == mActivePlayerId) { 46699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.w(TAG, getActivePlayer().getPackageName() + " is already the active player"); 46799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 46899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 46999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 47099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mActivePlayerId != NO_ACTIVE_PLAYER) getActivePlayer().unregisterCallback(); 47199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 47299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mActivePlayerId = playerId; 47399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker getActivePlayer().registerCallback(mMediaPlayerCallback); 47499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "setActivePlayer(): setting player to " + getActivePlayer().getPackageName()); 47599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 47699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Ensure that metadata is synced on the new player 47799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!getActivePlayer().isMetadataSynced()) { 47899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.w(TAG, "setActivePlayer(): Metadata not synced on new player"); 47999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 48099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 48199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 4820f5b951d00b5efb447774eabb899872e6cf83ccbtedwang if (Utils.isPtsTestMode()) { 4830f5b951d00b5efb447774eabb899872e6cf83ccbtedwang sendFolderUpdate(true, true, false); 4840f5b951d00b5efb447774eabb899872e6cf83ccbtedwang } 4850f5b951d00b5efb447774eabb899872e6cf83ccbtedwang 48699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker sendMediaUpdate(getActivePlayer().getCurrentMediaData()); 48799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 48899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 4897293a9642b571f3f2592575f7701a26e881c316eAjay Panicker // TODO (apanicke): Add logging for media key events in dumpsys 4907293a9642b571f3f2592575f7701a26e881c316eAjay Panicker void sendMediaKeyEvent(int key, boolean pushed) { 4917293a9642b571f3f2592575f7701a26e881c316eAjay Panicker d("sendMediaKeyEvent: key=" + key + " pushed=" + pushed); 4927293a9642b571f3f2592575f7701a26e881c316eAjay Panicker int action = pushed ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; 49399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker KeyEvent event = new KeyEvent(action, AvrcpPassthrough.toKeyCode(key)); 49499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mMediaSessionManager.dispatchMediaKeyEvent(event); 49599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 49699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 4970f5b951d00b5efb447774eabb899872e6cf83ccbtedwang private void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, 4980f5b951d00b5efb447774eabb899872e6cf83ccbtedwang boolean uids) { 4990f5b951d00b5efb447774eabb899872e6cf83ccbtedwang d("sendFolderUpdate"); 5000f5b951d00b5efb447774eabb899872e6cf83ccbtedwang if (mCallback == null) { 5010f5b951d00b5efb447774eabb899872e6cf83ccbtedwang return; 5020f5b951d00b5efb447774eabb899872e6cf83ccbtedwang } 5030f5b951d00b5efb447774eabb899872e6cf83ccbtedwang 5040f5b951d00b5efb447774eabb899872e6cf83ccbtedwang mCallback.run(availablePlayers, addressedPlayers, uids); 5050f5b951d00b5efb447774eabb899872e6cf83ccbtedwang } 5060f5b951d00b5efb447774eabb899872e6cf83ccbtedwang 50799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private void sendMediaUpdate(MediaData data) { 50899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker d("sendMediaUpdate"); 50999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (mCallback == null) { 51099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 51199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 51299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 51399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Always have items in the queue 51499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (data.queue.size() == 0) { 51599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "sendMediaUpdate: Creating a one item queue for a player with no queue"); 51699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker data.queue.add(data.metadata); 51799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 51899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 51999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mCallback.run(data); 52099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 52199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 52299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private final MediaSessionManager.OnActiveSessionsChangedListener 52399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker mActiveSessionsChangedListener = 52499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker new MediaSessionManager.OnActiveSessionsChangedListener() { 52599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 52699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onActiveSessionsChanged( 52799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker List<android.media.session.MediaController> newControllers) { 52899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker synchronized (MediaPlayerList.this) { 52999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.v(TAG, "onActiveSessionsChanged: number of controllers: " 53099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + newControllers.size()); 53199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (newControllers.size() == 0) return; 53299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 53399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Apps are allowed to have multiple MediaControllers. If an app does have 53499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // multiple controllers then newControllers contains them in highest 53599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // priority order. Since we only want to keep the highest priority one, 53699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // we keep track of which controllers we updated and skip over ones 53799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // we've already looked at. 53899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker HashSet<String> addedPackages = new HashSet<String>(); 53999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 54099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (int i = 0; i < newControllers.size(); i++) { 54199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.d(TAG, "onActiveSessionsChanged: controller: " 54299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + newControllers.get(i).getPackageName()); 54399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (addedPackages.contains(newControllers.get(i).getPackageName())) { 54499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker continue; 54599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 54699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 54799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker addedPackages.add(newControllers.get(i).getPackageName()); 54899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker addMediaPlayer(newControllers.get(i)); 54999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 55099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 55199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 55299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }; 55399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 55499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Write a test that tests uninstalling the active session 55599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private final BroadcastReceiver mPackageChangedBroadcastReceiver = new BroadcastReceiver() { 55699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 55799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onReceive(Context context, Intent intent) { 55899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String action = intent.getAction(); 55999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.v(TAG, "mPackageChangedBroadcastReceiver: action: " + action); 56099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 56199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 56299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) { 56399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) return; 56499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 56599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String packageName = intent.getData().getSchemeSpecificPart(); 566cd53caa864581bfc679c065696fe3506f46d8d31Hansong Zhang if (packageName != null && mMediaPlayerIds.containsKey(packageName)) { 56799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker removeMediaPlayer(mMediaPlayerIds.get(packageName)); 56899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 56999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } else if (action.equals(Intent.ACTION_PACKAGE_ADDED) 57099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker || action.equals(Intent.ACTION_PACKAGE_CHANGED)) { 57199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker String packageName = intent.getData().getSchemeSpecificPart(); 57299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (packageName != null) { 57399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (DEBUG) Log.d(TAG, "Name of package changed: " + packageName); 57499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Handle either updating or adding the new package. 57599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Check if its browsable and send the UIDS changed to update the 57699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // root folder 57799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 57899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 57999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 58099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }; 58199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 58299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private final MediaPlayerWrapper.Callback mMediaPlayerCallback = 58399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker new MediaPlayerWrapper.Callback() { 58499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 58599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void mediaUpdatedCallback(MediaData data) { 58699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (data.metadata == null) { 58799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.d(TAG, "mediaUpdatedCallback(): metadata is null"); 58899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 58999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 59099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 59199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (data.state == null) { 59299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.w(TAG, "mediaUpdatedCallback(): Tried to update with null state"); 59399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 59499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 59599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 59699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker sendMediaUpdate(data); 59799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 59899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }; 59999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 60099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private final MediaSessionManager.Callback mButtonDispatchCallback = 60199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker new MediaSessionManager.Callback() { 60299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 60399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onMediaKeyEventDispatched(KeyEvent event, MediaSession.Token token) { 60499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add logging for these 60599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 60699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 60799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 60899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onMediaKeyEventDispatched(KeyEvent event, ComponentName receiver) { 60999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add logging for these 61099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 61199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 61299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 61399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onAddressedPlayerChanged(MediaSession.Token token) { 61499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker android.media.session.MediaController controller = 61599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker new android.media.session.MediaController(mContext, token); 61699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 61799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayerIds.containsKey(controller.getPackageName())) { 61899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // Since we have a controller, we can try to to recover by adding the 61999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // player and then setting it as active. 62099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.w(TAG, "onAddressedPlayerChanged(Token): Addressed Player " 62199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + "changed to a player we didn't have a session for"); 62299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker addMediaPlayer(controller); 62399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 62499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 62599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "onAddressedPlayerChanged: token=" + controller.getPackageName()); 62699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker setActivePlayer(mMediaPlayerIds.get(controller.getPackageName())); 62799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 62899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 62999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker @Override 63099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker public void onAddressedPlayerChanged(ComponentName receiver) { 63199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (receiver == null) { 63299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 63399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 63499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 63599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (!mMediaPlayerIds.containsKey(receiver.getPackageName())) { 63699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker e("onAddressedPlayerChanged(Component): Addressed Player " 63799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker + "changed to a player we don't have a session for"); 63899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker return; 63999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 64099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 64199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.i(TAG, "onAddressedPlayerChanged: component=" + receiver.getPackageName()); 64299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker setActivePlayer(mMediaPlayerIds.get(receiver.getPackageName())); 64399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 64499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker }; 64599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 64699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 64799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker void dump(StringBuilder sb) { 6485bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append("List of MediaControllers: size=" + mMediaPlayers.size() + "\n"); 6495bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker for (int id : mMediaPlayers.keySet()) { 6505bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker if (id == mActivePlayerId) { 6515bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append("<Active> "); 65299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 6535bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker MediaPlayerWrapper player = mMediaPlayers.get(id); 6545bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append(" Media Player " + id + ": " + player.getPackageName() + "\n"); 6555bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append(player.toString().replaceAll("(?m)^", " ")); 6565bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append("\n"); 65799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 6585bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker 6595bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append("List of Browsers: size=" + mBrowsablePlayers.size() + "\n"); 66099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) { 6615bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append(player.toString().replaceAll("(?m)^", " ")); 6625bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker sb.append("\n"); 66399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 66499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add media key events 66599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add last sent data 66699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker // TODO (apanicke): Add addressed player history 66799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 66899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 66999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static void e(String message) { 67099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (sTesting) { 67199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.wtfStack(TAG, message); 67299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } else { 67399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.e(TAG, message); 67499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 67599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 67699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker 67799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker private static void d(String message) { 67899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker if (DEBUG) { 67999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker Log.d(TAG, message); 68099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 68199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker } 68299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker} 683