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