164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer/* 264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Copyright (C) 2016 The Android Open Source Project 364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * 464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License"); 564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * you may not use this file except in compliance with the License. 664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * You may obtain a copy of the License at 764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * 864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * http://www.apache.org/licenses/LICENSE-2.0 964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * 1064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Unless required by applicable law or agreed to in writing, software 1164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS, 1264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * See the License for the specific language governing permissions and 1464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * limitations under the License. 1564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 1664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerpackage com.android.car.media; 1764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 1864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.app.SearchManager; 1964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.ComponentName; 2064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.Context; 2164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.Intent; 2264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.SharedPreferences; 2364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.pm.ApplicationInfo; 2464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.pm.PackageManager; 2564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.pm.ResolveInfo; 2664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.pm.ServiceInfo; 2764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.res.Resources; 2864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.content.res.TypedArray; 2964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.media.browse.MediaBrowser; 3064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.media.session.MediaController; 3164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.media.session.MediaSession; 3264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.media.session.PlaybackState; 3364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.os.Bundle; 3464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.service.media.MediaBrowserService; 3564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.text.TextUtils; 3664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport android.util.Log; 3764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 3864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport java.lang.ref.WeakReference; 3964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport java.util.ArrayList; 4064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerimport java.util.List; 4164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 4264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer/** 4364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Manages which media app we should connect to. The manager also retrieves various attributes 4464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * from the media app and share among different components in GearHead media app. 458189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez * 468189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez * @deprecated This manager is being replaced by {@link com.android.car.media.common.PlaybackModel}. 4764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 488189d452317e04e1c1852d2d3a2e9da3e0295147Roberto Perez@Deprecated 4964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyerpublic class MediaManager { 5064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String TAG = "GH.MediaManager"; 5164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String PREFS_FILE_NAME = "MediaClientManager.Preferences"; 5264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** The package of the most recently used media component **/ 5364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String PREFS_KEY_PACKAGE = "media_package"; 5464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** The class of the most recently used media class **/ 5564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String PREFS_KEY_CLASS = "media_class"; 5664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** Third-party defined application theme to use **/ 5764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String THEME_META_DATA_NAME = "com.google.android.gms.car.application.theme"; 5864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 5964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final String KEY_MEDIA_COMPONENT = "media_component"; 6064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** Intent extra specifying the package with the MediaBrowser **/ 6164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final String KEY_MEDIA_PACKAGE = "media_package"; 6264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** Intent extra specifying the MediaBrowserService **/ 6364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final String KEY_MEDIA_CLASS = "media_class"; 6464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 6564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 6664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Flag for when GSA is not 100% confident on the query and therefore, the result in the 6764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * {@link #KEY_MEDIA_PACKAGE_FROM_GSA} should be ignored. 6864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 6964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String KEY_IGNORE_ORIGINAL_PKG = 7064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer "com.google.android.projection.gearhead.ignore_original_pkg"; 7164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 7264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 7364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Intent extra specifying the package name of the media app that should handle 7464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * {@link android.provider.MediaStore#INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH}. This must match 7564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * KEY_PACKAGE defined in ProjectionIntentStarter in GSA. 7664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 7764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final String KEY_MEDIA_PACKAGE_FROM_GSA = 78ef10fab856cafbfaa89bd79bb7b95fca7f58b2ddYao Chen "android.car.intent.extra.MEDIA_PACKAGE"; 7964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 8064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String GOOGLE_PLAY_MUSIC_PACKAGE = "com.google.android.music"; 8164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Extras along with the Knowledge Graph that are not meant to be seen by external apps. 8264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final String[] INTERNAL_EXTRAS = {"KEY_LAUNCH_HANDOVER_UNDERNEATH", 8364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer "com.google.android.projection.gearhead.ignore_original_pkg"}; 8464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 8564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static final Intent MEDIA_BROWSER_INTENT = 8664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer new Intent(MediaBrowserService.SERVICE_INTERFACE); 8764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static MediaManager sInstance; 8864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 8964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final MediaController.Callback mMediaControllerCallback = 9064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer new MediaManagerCallback(this); 9164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final MediaBrowser.ConnectionCallback mMediaBrowserConnectionCallback = 9264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer new MediaManagerConnectionCallback(this); 9364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 9464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public interface Listener { 9564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer void onMediaAppChanged(ComponentName componentName); 9664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 9764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 9864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Called when we want to show a message on playback screen. 9964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * @param msg if null, dismiss any previous message and 10064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * restore the track title and subtitle. 10164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 10264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer void onStatusMessageChanged(String msg); 10364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 10464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 10564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 10664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * An adapter interface to abstract the specifics of how media services are queried. This allows 10764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * for Vanagon to query for allowed media services without the need to connect to carClientApi. 10864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 10964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public interface ServiceAdapter { 11064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer List<ResolveInfo> queryAllowedServices(Intent providerIntent); 11164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 11264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 11364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private int mPrimaryColor; 11464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private int mPrimaryColorDark; 11564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private int mAccentColor; 11664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private CharSequence mName; 11764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 11864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final Context mContext; 11964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final List<Listener> mListeners = new ArrayList<>(); 12064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 12164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private ServiceAdapter mServiceAdapter; 12264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private Intent mPendingSearchIntent; 12364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 12464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private MediaController mController; 12564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private MediaBrowser mBrowser; 12664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private ComponentName mCurrentComponent; 12764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private PendingMsg mPendingMsg; 12864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 12964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public synchronized static MediaManager getInstance(Context context) { 13064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (sInstance == null) { 13164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer sInstance = new MediaManager(context.getApplicationContext()); 13264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 13364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return sInstance; 13464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 13564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 13664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private MediaManager(Context context) { 13764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mContext = context; 13864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 13964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Set some sane default values for the attributes 14064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mName = ""; 14164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer int color = context.getResources().getColor(android.R.color.background_dark); 14264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPrimaryColor = color; 14364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mAccentColor = color; 14464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPrimaryColorDark = color; 14564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 14664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 14764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 14864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Returns the default component used to load media. 14964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 15064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public ComponentName getDefaultComponent(ServiceAdapter serviceAdapter) { 15164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer SharedPreferences prefs = mContext 15264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer .getSharedPreferences(PREFS_FILE_NAME, Context.MODE_PRIVATE); 15364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer String packageName = prefs.getString(PREFS_KEY_PACKAGE, null); 15464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer String className = prefs.getString(PREFS_KEY_CLASS, null); 15564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer final Intent providerIntent = new Intent(MediaBrowserService.SERVICE_INTERFACE); 15664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer List<ResolveInfo> mediaApps = serviceAdapter.queryAllowedServices(providerIntent); 15764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 15864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // check if the previous component we connected to is still valid. 15964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (packageName != null && className != null) { 16064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer boolean componentValid = false; 16164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer for (ResolveInfo info : mediaApps) { 16264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (info.serviceInfo.packageName.equals(packageName) 16364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer && info.serviceInfo.name.equals(className)) { 16464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer componentValid = true; 16564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 16664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 16764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // if not valid, null it and we will bring up the lens switcher or connect to another 16864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // app (this may happen when the app has been uninstalled) 16964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (!componentValid) { 17064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer packageName = null; 17164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer className = null; 17264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 17364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 17464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 17564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // If there are no apps used before or previous app is not valid, 17664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // try to connect to a supported media app. 17764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (packageName == null || className == null) { 17864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Only one app installed, connect to it. 17964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mediaApps.size() == 1) { 18064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ResolveInfo info = mediaApps.get(0); 18164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer packageName = info.serviceInfo.packageName; 18264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer className = info.serviceInfo.name; 18364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } else { 18464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // there are '0' or >1 media apps installed; don't know what to run 18564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return null; 18664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 18764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 18864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return new ComponentName(packageName, className); 18964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 19064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 19164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 19264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Connects to the most recently used media app if it exists and return true. 19364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Otherwise check the number of supported media apps installed, 19464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * if only one installed, connect to it return true. Otherwise return false. 19564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 19664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public boolean connectToMostRecentMediaComponent(ServiceAdapter serviceAdapter) { 19764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ComponentName component = getDefaultComponent(serviceAdapter); 19864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (component != null) { 19964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer setMediaClientComponent(serviceAdapter, component); 20064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return true; 20164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 20264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return false; 20364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 20464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 20564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public ComponentName getCurrentComponent() { 20664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return mCurrentComponent; 20764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 20864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 20964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void setMediaClientComponent(ComponentName component) { 21064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer setMediaClientComponent(null, component); 21164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 21264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 21364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 21464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Change the media component. This will connect to a {@link android.media.browse.MediaBrowser} if necessary. 21564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * All registered listener will be updated with the new component. 21664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 21764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void setMediaClientComponent(ServiceAdapter serviceAdapter, ComponentName component) { 21864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.VERBOSE)) { 21964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.v(TAG, "setMediaClientComponent(), " 22064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer + "component: " + (component == null ? "<< NULL >>" : component.toString())); 22164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 22264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 22364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (component == null) { 22464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 22564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 22664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 22764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // mController will be set to null if previously connected media session has crashed. 22864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mCurrentComponent != null && mCurrentComponent.equals(component) 22964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer && mController != null) { 23064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 23164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "Already connected to " + component.toString()); 23264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 23364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 23464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 23564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 23664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mCurrentComponent = component; 23764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mServiceAdapter = serviceAdapter; 23864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer disconnectCurrentBrowser(); 23964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer updateClientPackageAttributes(mCurrentComponent); 24064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 24164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mController != null) { 24264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController.unregisterCallback(mMediaControllerCallback); 24364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController = null; 24464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 24564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser = new MediaBrowser(mContext, component, mMediaBrowserConnectionCallback, null); 24664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 24764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "Connecting to " + component.toString()); 24864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 24964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser.connect(); 25064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 25164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer writeComponentToPrefs(component); 25264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 25364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ArrayList<Listener> temp = new ArrayList<Listener>(mListeners); 25464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer for (Listener listener : temp) { 25564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer listener.onMediaAppChanged(mCurrentComponent); 25664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 25764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 25864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 25964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 26064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Processes the search intent using the current media app. If it's not connected yet, store it 26164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * in the {@code mPendingSearchIntent} and process it when the app is connected. 26264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * 26364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * @param intent The intent containing the query and 26464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH action 26564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 26664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void processSearchIntent(Intent intent) { 26764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.VERBOSE)) { 26864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.v(TAG, "processSearchIntent(), query: " 26964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer + (intent == null ? "<< NULL >>" : intent.getStringExtra(SearchManager.QUERY))); 27064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 27164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (intent == null) { 27264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 27364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 27464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPendingSearchIntent = intent; 27564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 27664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer String mediaPackageName; 27764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (intent.getBooleanExtra(KEY_IGNORE_ORIGINAL_PKG, false)) { 27864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 27964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "Ignoring package from gsa and falling back to default media app"); 28064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 28164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mediaPackageName = null; 28264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } else if (intent.hasExtra(KEY_MEDIA_PACKAGE_FROM_GSA)) { 28364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Legacy way of piping through the media app package. 28464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mediaPackageName = intent.getStringExtra(KEY_MEDIA_PACKAGE_FROM_GSA); 28564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 28664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "Package from extras: " + mediaPackageName); 28764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 28864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } else { 28964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mediaPackageName = intent.getPackage(); 29064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 29164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "Package from getPackage(): " + mediaPackageName); 29264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 29364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 29464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 29564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mediaPackageName != null && mCurrentComponent != null 29664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer && !mediaPackageName.equals(mCurrentComponent.getPackageName())) { 29764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer final ComponentName componentName = 29864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer getMediaBrowserComponent(mServiceAdapter, mediaPackageName); 29964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (componentName == null) { 30064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.w(TAG, "There are no matching media app to handle intent: " + intent); 30164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 30264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 30364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer setMediaClientComponent(mServiceAdapter, componentName); 30464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // It's safe to return here as pending search intent will be processed 30564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // when newly created media controller for the new media component is connected. 30664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 30764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 30864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 30964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer String query = mPendingSearchIntent.getStringExtra(SearchManager.QUERY); 31064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mController != null) { 31164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController.getTransportControls().pause(); 31264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPendingMsg = new PendingMsg(PendingMsg.STATUS_UPDATE, 31364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mContext.getResources().getString(R.string.loading)); 31464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer notifyStatusMessage(mPendingMsg.mMsg); 31564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Bundle extras = mPendingSearchIntent.getExtras(); 31664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Remove two extras that are not meant to be seen by external apps. 31764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (!GOOGLE_PLAY_MUSIC_PACKAGE.equals(mediaPackageName)) { 31864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer for (String key : INTERNAL_EXTRAS) { 31964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer extras.remove(key); 32064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 32164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 32264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController.getTransportControls().playFromSearch(query, extras); 32364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPendingSearchIntent = null; 32464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } else { 32564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 32664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "No controller for search intent; save it for later"); 32764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 32864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 32964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 33064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 33164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 33264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private ComponentName getMediaBrowserComponent(ServiceAdapter serviceAdapter, 33364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer final String packageName) { 33464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer List<ResolveInfo> queryResults = serviceAdapter.queryAllowedServices(MEDIA_BROWSER_INTENT); 33564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (queryResults != null) { 33664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer for (int i = 0, N = queryResults.size(); i < N; ++i) { 33764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer final ResolveInfo ri = queryResults.get(i); 33864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (ri != null && ri.serviceInfo != null 33964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer && ri.serviceInfo.packageName.equals(packageName)) { 34064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return new ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name); 34164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 34264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 34364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 34464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return null; 34564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 34664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 34764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 34864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Add a listener to get media app changes. 34964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Your listener will be called with the initial values when the listener is added. 35064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 35164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void addListener(Listener listener) { 35264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mListeners.add(listener); 35364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.VERBOSE)) { 35464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.v(TAG, "addListener(); count: " + mListeners.size()); 35564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 35664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 35764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mCurrentComponent != null) { 35864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer listener.onMediaAppChanged(mCurrentComponent); 35964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 36064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 36164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mPendingMsg != null) { 36264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer listener.onStatusMessageChanged(mPendingMsg.mMsg); 36364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 36464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 36564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 36664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void removeListener(Listener listener) { 36764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mListeners.remove(listener); 36864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 36964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.VERBOSE)) { 37064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.v(TAG, "removeListener(); count: " + mListeners.size()); 37164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 37264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 37364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mListeners.size() == 0) { 37464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 37564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.d(TAG, "no manager listeners; destroy manager instance"); 37664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 37764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 37864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer synchronized (MediaManager.class) { 37964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer sInstance = null; 38064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 38164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 38264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mBrowser != null) { 38364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser.disconnect(); 38464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 38564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 38664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 38764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 38864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public CharSequence getMediaClientName() { 38964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return mName; 39064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 39164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 39264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public int getMediaClientPrimaryColor() { 39364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return mPrimaryColor; 39464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 39564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 39664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public int getMediaClientPrimaryColorDark() { 39764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return mPrimaryColorDark; 39864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 39964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 40064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public int getMediaClientAccentColor() { 40164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return mAccentColor; 40264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 40364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 40464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void writeComponentToPrefs(ComponentName componentName) { 40564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Store selected media service to shared preference. 40664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer SharedPreferences prefs = mContext 40764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer .getSharedPreferences(PREFS_FILE_NAME, Context.MODE_PRIVATE); 40864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer SharedPreferences.Editor editor = prefs.edit(); 40964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer editor.putString(PREFS_KEY_PACKAGE, componentName.getPackageName()); 41064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer editor.putString(PREFS_KEY_CLASS, componentName.getClassName()); 41164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer editor.apply(); 41264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 41364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 41464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer /** 41564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer * Disconnect from the current media browser service if any, and notify the listeners. 41664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer */ 41764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void disconnectCurrentBrowser() { 41864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mBrowser != null) { 41964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser.disconnect(); 42064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser = null; 42164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 42264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 42364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 42464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void updateClientPackageAttributes(ComponentName componentName) { 42564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer TypedArray ta = null; 42664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer try { 42764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer String packageName = componentName.getPackageName(); 42864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ApplicationInfo applicationInfo = 42964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mContext.getPackageManager().getApplicationInfo(packageName, 43064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer PackageManager.GET_META_DATA); 43164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo( 43264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer componentName, PackageManager.GET_META_DATA); 43364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 43464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Get the proper app name, check service label, then application label. 43564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer CharSequence name = ""; 43664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (serviceInfo.labelRes != 0) { 43764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer name = serviceInfo.loadLabel(mContext.getPackageManager()); 43864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } else if (applicationInfo.labelRes != 0) { 43964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer name = applicationInfo.loadLabel(mContext.getPackageManager()); 44064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 44164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (TextUtils.isEmpty(name)) { 44264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer name = mContext.getResources().getString(R.string.unknown_media_provider_name); 44364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 44464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mName = name; 44564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 44664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Get the proper theme, check theme for service, then application. 44764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer int appTheme = 0; 44864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (serviceInfo.metaData != null) { 44964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer appTheme = serviceInfo.metaData.getInt(THEME_META_DATA_NAME); 45064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 45164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (appTheme == 0 && applicationInfo.metaData != null) { 45264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer appTheme = applicationInfo.metaData.getInt(THEME_META_DATA_NAME); 45364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 45464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (appTheme == 0) { 45564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer appTheme = applicationInfo.theme; 45664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 45764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 45864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Context packageContext = mContext.createPackageContext(packageName, 0); 45964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer packageContext.setTheme(appTheme); 46064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Resources.Theme theme = packageContext.getTheme(); 46164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ta = theme.obtainStyledAttributes(new int[] { 46264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer android.R.attr.colorPrimary, 46364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer android.R.attr.colorAccent, 46464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer android.R.attr.colorPrimaryDark 46564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer }); 46664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer int defaultColor = 46764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mContext.getResources().getColor(android.R.color.background_dark); 46864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPrimaryColor = ta.getColor(0, defaultColor); 46964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mAccentColor = ta.getColor(1, defaultColor); 47064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPrimaryColorDark = ta.getColor(2, defaultColor); 47164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } catch (PackageManager.NameNotFoundException e) { 47264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.e(TAG, "Unable to update media client package attributes.", e); 47364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } finally { 47464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (ta != null) { 47564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer ta.recycle(); 47664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 47764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 47864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 47964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 48064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void notifyStatusMessage(String str) { 48164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer for (Listener l : mListeners) { 48264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer l.onStatusMessageChanged(str); 48364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 48464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 48564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 48664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void doPlaybackStateChanged(PlaybackState playbackState) { 48764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Display error message in MediaPlaybackFragment. 48864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mPendingMsg == null) { 48964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 49064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 49164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // Dismiss the error msg if any, 49264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // and dismiss status update msg if the state is now playing 49364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if ((mPendingMsg.mType == PendingMsg.ERROR) || 49464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer (playbackState.getState() == PlaybackState.STATE_PLAYING 49564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer && mPendingMsg.mType == PendingMsg.STATUS_UPDATE)) { 49664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mPendingMsg = null; 49764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer notifyStatusMessage(null); 49864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 49964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 50064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 50164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void doOnSessionDestroyed() { 50264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (Log.isLoggable(TAG, Log.VERBOSE)) { 50364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.v(TAG, "Media session destroyed"); 50464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 50564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (mController != null) { 50664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController.unregisterCallback(mMediaControllerCallback); 50764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 50864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController = null; 50964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mServiceAdapter = null; 51064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 51164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 51264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void doOnConnected() { 51364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // existing mController has been disconnected before we call MediaBrowser.connect() 51464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaSession.Token token = mBrowser.getSessionToken(); 51564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (token == null) { 51664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.e(TAG, "Media session token is null"); 51764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 51864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 51964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController = new MediaController(mContext, token); 52064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mController.registerCallback(mMediaControllerCallback); 52164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer processSearchIntent(mPendingSearchIntent); 52264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 52364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 52464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private void doOnConnectionFailed() { 52564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer Log.w(TAG, "Media browser connection FAILED!"); 52664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer // disconnect anyway to make sure we get into a sanity state 52764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser.disconnect(); 52864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mBrowser = null; 52964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 53064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 53164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static class PendingMsg { 53264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final int ERROR = 0; 53364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public static final int STATUS_UPDATE = 1; 53464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 53564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public int mType; 53664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public String mMsg; 53764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public PendingMsg(int type, String msg) { 53864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mType = type; 53964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mMsg = msg; 54064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 54164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 54264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 54364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static class MediaManagerCallback extends MediaController.Callback { 54464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final WeakReference<MediaManager> mWeakCallback; 54564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 54664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaManagerCallback(MediaManager callback) { 54764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mWeakCallback = new WeakReference<>(callback); 54864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 54964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 55064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer @Override 55164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void onPlaybackStateChanged(PlaybackState playbackState) { 55264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaManager callback = mWeakCallback.get(); 55364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (callback == null) { 55464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 55564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 55664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer callback.doPlaybackStateChanged(playbackState); 55764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 55864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 55964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer @Override 56064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void onSessionDestroyed() { 56164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaManager callback = mWeakCallback.get(); 56264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (callback == null) { 56364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 56464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 56564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer callback.doOnSessionDestroyed(); 56664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 56764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 56864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 56964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private static class MediaManagerConnectionCallback extends MediaBrowser.ConnectionCallback { 57064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private final WeakReference<MediaManager> mWeakCallback; 57164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 57264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer private MediaManagerConnectionCallback(MediaManager callback) { 57364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer mWeakCallback = new WeakReference<>(callback); 57464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 57564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 57664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer @Override 57764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void onConnected() { 57864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaManager callback = mWeakCallback.get(); 57964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (callback == null) { 58064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 58164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 58264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer callback.doOnConnected(); 58364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 58464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 58564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer @Override 58664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void onConnectionSuspended() {} 58764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer 58864b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer @Override 58964b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer public void onConnectionFailed() { 59064b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer MediaManager callback = mWeakCallback.get(); 59164b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer if (callback == null) { 59264b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer return; 59364b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 59464b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer callback.doOnConnectionFailed(); 59564b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 59664b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer } 59764b173f33aa2d98ea00b9113b52e6f8cb35589adRakesh Iyer} 598