1214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer/*
2214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * Copyright (c) 2016, The Android Open Source Project
3214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer *
4214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License");
5214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * you may not use this file except in compliance with the License.
6214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * You may obtain a copy of the License at
7214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer *
8214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer *     http://www.apache.org/licenses/LICENSE-2.0
9214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer *
10214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * Unless required by applicable law or agreed to in writing, software
11214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS,
12214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * See the License for the specific language governing permissions and
14214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * limitations under the License.
15214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer */
16214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerpackage com.android.car.stream.media;
17214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
18214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.content.Context;
19214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.graphics.Bitmap;
20214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.media.MediaDescription;
21214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.media.MediaMetadata;
22214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.media.session.PlaybackState;
23214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.net.Uri;
24214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.os.Handler;
25214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.os.Message;
26214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.support.annotation.NonNull;
27214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.support.annotation.Nullable;
28214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.text.TextUtils;
29214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport android.util.Log;
30214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport com.android.car.apps.common.BitmapDownloader;
31214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport com.android.car.apps.common.BitmapWorkerOptions;
32214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerimport com.android.car.stream.R;
33214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
34214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer/**
35214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * An service which connects to {@link MediaStateManager} for media updates (playback state and
36214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * metadata) and notifies listeners for these changes.
37214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer * <p/>
38214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer */
39214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyerpublic class MediaPlaybackMonitor implements MediaStateManager.Listener {
40214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    protected static final String TAG = "MediaPlaybackMonitor";
41214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
42214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    // MSG for metadata update handler
43214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private static final int MSG_UPDATE_METADATA = 1;
44214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private static final int MSG_IMAGE_DOWNLOADED = 2;
45214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private static final int MSG_NEW_ALBUM_ART_RECEIVED = 3;
46214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
47214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public interface MediaPlaybackMonitorListener {
48214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        void onPlaybackStateChanged(PlaybackState state);
49214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
50214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        void onMetadataChanged(String title, String text, Bitmap art, int color, String appName);
51214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
52214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        void onAlbumArtUpdated(Bitmap albumArt);
53214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
54214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        void onNewAppConnected();
55214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
56214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        void removeMediaStreamCard();
57214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
58214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
59214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private static final String[] PREFERRED_BITMAP_ORDER = {
60214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_ALBUM_ART,
61214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_ART,
62214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_DISPLAY_ICON
63214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    };
64214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
65214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private static final String[] PREFERRED_URI_ORDER = {
66214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
67214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_ART_URI,
68214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
69214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    };
70214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
71214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private MediaMetadata mCurrentMetadata;
72214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private MediaStatusUpdateHandler mMediaStatusUpdateHandler;
73214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private MediaAppInfo mCurrentMediaAppInfo;
74214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private MediaPlaybackMonitorListener mMonitorListener;
75214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
76214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private Context mContext;
77214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
78214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private final int mIconSize;
79214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
80214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public MediaPlaybackMonitor(Context context, @NonNull MediaPlaybackMonitorListener callback) {
81214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        mContext = context;
82214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        mMonitorListener = callback;
83214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.stream_media_icon_size);
84214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
85214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
86214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public final void start() {
87214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        mMediaStatusUpdateHandler = new MediaStatusUpdateHandler();
88214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
89214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
90214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public final void stop() {
91214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (mMediaStatusUpdateHandler != null) {
92214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMediaStatusUpdateHandler.removeCallbacksAndMessages(null);
93214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMediaStatusUpdateHandler = null;
94214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
95214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
96214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
97214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    @Override
98214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public void onMediaSessionConnected(PlaybackState state, MediaMetadata metaData,
99214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaAppInfo appInfo) {
100214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
101214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.d(TAG, "MediaSession onConnected called");
102214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
103214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
104214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        // If the current media app is not the same as the new media app, reset
105214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        // the media app in MediaStreamManager
106214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (mCurrentMediaAppInfo == null
107214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                || !mCurrentMediaAppInfo.getPackageName().equals(appInfo.getPackageName())) {
108214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMonitorListener.onNewAppConnected();
109214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (mMediaStatusUpdateHandler != null) {
110214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                mMediaStatusUpdateHandler.removeCallbacksAndMessages(null);
111214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
112214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mCurrentMediaAppInfo = appInfo;
113214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
114214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
115214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (metaData != null) {
116214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            onMetadataChanged(metaData);
117214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
118214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
119214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (state != null) {
120214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            onPlaybackStateChanged(state);
121214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
122214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
123214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
124214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    @Override
125214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public void onPlaybackStateChanged(@Nullable PlaybackState state) {
126214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
127214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.d(TAG, "onPlaybackStateChanged called " + state.getState());
128214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
129214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
130214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (state == null) {
131214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.w(TAG, "playback state is null in onPlaybackStateChanged");
132214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMonitorListener.removeMediaStreamCard();
133214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            return;
134214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
135214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
136214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (mMonitorListener != null) {
137214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMonitorListener.onPlaybackStateChanged(state);
138214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
139214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
140214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
141214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    @Override
142214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public void onMetadataChanged(@Nullable MediaMetadata metadata) {
143214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
144214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.d(TAG, "onMetadataChanged called");
145214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
146214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (metadata == null) {
147214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMonitorListener.removeMediaStreamCard();
148214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            return;
149214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
150214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
151214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.d(TAG, "received " + metadata.getDescription());
152214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
153214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        // Compare the new metadata and the last we have posted notification for. If both
154214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        // metadata and album art are the same, just ignore and return. If the album art is new,
155214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        // update the stream item with the new album art.
156214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        MediaDescription currentDescription = mCurrentMetadata == null ?
157214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                null : mCurrentMetadata.getDescription();
158214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
159214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (!MediaUtils.isSameMediaDescription(metadata.getDescription(), currentDescription)) {
160214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Message msg =
161214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    mMediaStatusUpdateHandler.obtainMessage(MSG_UPDATE_METADATA, metadata);
162214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            // Remove obsolete notifications  in the queue.
163214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMediaStatusUpdateHandler.removeMessages(MSG_UPDATE_METADATA);
164214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMediaStatusUpdateHandler.sendMessage(msg);
165214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        } else {
166214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Bitmap newBitmap = metadata.getDescription().getIconBitmap();
167214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (newBitmap == null) {
168214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                return;
169214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
170214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (newBitmap.sameAs(mMediaStatusUpdateHandler.getCurrentIcon())) {
171214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                if (Log.isLoggable(TAG, Log.DEBUG)) {
172214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    Log.d(TAG, "Received duplicate metadata, ignoring...");
173214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                }
174214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            } else {
175214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                // same metadata, but new album art
176214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                if (Log.isLoggable(TAG, Log.DEBUG)) {
177214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    Log.d(TAG, "Received metadata with new album art");
178214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                }
179214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                Message msg = mMediaStatusUpdateHandler
180214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        .obtainMessage(MSG_NEW_ALBUM_ART_RECEIVED, newBitmap);
181214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                mMediaStatusUpdateHandler.removeMessages(MSG_NEW_ALBUM_ART_RECEIVED);
182214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                mMediaStatusUpdateHandler.sendMessage(msg);
183214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
184214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
185214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
186214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
187214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    @Override
188214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    public void onSessionDestroyed() {
189214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
190214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Log.d(TAG, "Media session destroyed");
191214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
192214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        mMonitorListener.removeMediaStreamCard();
193214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
194214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
195214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private class BitmapCallback extends BitmapDownloader.BitmapCallback {
196214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        final private int mSeq;
197214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
198214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        public BitmapCallback(int seq) {
199214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mSeq = seq;
200214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
201214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
202214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        @Override
203214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        public void onBitmapRetrieved(Bitmap bitmap) {
204214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (mMediaStatusUpdateHandler == null) {
205214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                if (Log.isLoggable(TAG, Log.DEBUG)) {
206214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    Log.d(TAG, "The callback comes after we finish");
207214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                }
208214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                return;
209214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
210214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            Message msg = mMediaStatusUpdateHandler.obtainMessage(MSG_IMAGE_DOWNLOADED,
211214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    mSeq, 0, bitmap);
212214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMediaStatusUpdateHandler.sendMessage(msg);
213214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
214214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
215214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
216214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    private class MediaStatusUpdateHandler extends Handler {
217214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private int mSeq = 0;
218214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private BitmapCallback mCallback;
219214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private MediaMetadata mMetadata;
220214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private String mTitle;
221214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private String mSubtitle;
222214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private Bitmap mIcon;
223214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private Uri mIconUri;
224214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private final BitmapDownloader mDownloader = BitmapDownloader.getInstance(mContext);
225214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
226214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private void extractMetadata(MediaMetadata metadata) {
227214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (metadata == mMetadata) {
228214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                // We are up to date and must return here, because we've already recycled the bitmap
229214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                // inside it.
230214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                return;
231214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
232214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            // keep a reference so we know which metadata we have stored.
233214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mMetadata = metadata;
234214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaDescription description = metadata.getDescription();
235214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mTitle = description.getTitle() == null ? null : description.getTitle().toString();
236214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mSubtitle = description.getSubtitle() == null ?
237214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    null : description.getSubtitle().toString();
238214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            final Bitmap originalBitmap = getMetadataBitmap(metadata);
239214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (originalBitmap != null) {
240214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                mIcon = originalBitmap;
241214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            } else {
242214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                mIcon = null;
243214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
244214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            mIconUri = getMetadataIconUri(metadata);
245214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            if (Log.isLoggable(TAG, Log.DEBUG)) {
246214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                Log.d(TAG, "Album Art Uri: " + mIconUri);
247214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
248214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
249214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
250214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private Uri getMetadataIconUri(MediaMetadata metadata) {
251214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            // Get the best Uri we can find
252214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
253214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                String iconUri = metadata.getString(PREFERRED_URI_ORDER[i]);
254214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                if (!TextUtils.isEmpty(iconUri)) {
255214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    return Uri.parse(iconUri);
256214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                }
257214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
258214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            return null;
259214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
260214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
261214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        private Bitmap getMetadataBitmap(MediaMetadata metadata) {
262214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            // Get the best art bitmap we can find
263214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
264214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                Bitmap bitmap = metadata.getBitmap(PREFERRED_BITMAP_ORDER[i]);
265214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                if (bitmap != null) {
266214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (Log.isLoggable(TAG, Log.DEBUG)) {
267214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        Log.d(TAG, "Retrieved bitmap type: " + PREFERRED_BITMAP_ORDER[i]
268214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                + " w: " + bitmap.getWidth()
269214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                + " h: " + bitmap.getHeight());
270214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
271214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    return bitmap;
272214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                }
273214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
274214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            return null;
275214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
276214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
277214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        public Bitmap getCurrentIcon() {
278214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            return mIcon;
279214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
280214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
281214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        @Override
282214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        public void handleMessage(Message msg) {
283214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            MediaAppInfo mediaAppInfo = mCurrentMediaAppInfo;
284214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            int color = mediaAppInfo.getMediaClientAccentColor();
285214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            String appName = mediaAppInfo.getAppName();
286214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            switch (msg.what) {
287214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                case MSG_UPDATE_METADATA:
288214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    mSeq++;
289214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    MediaMetadata metadata = (MediaMetadata) msg.obj;
290214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (metadata == null) {
291214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        Log.w(TAG, "media metadata is null!");
292214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        return;
293214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
294214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    extractMetadata(metadata);
295214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (mCallback != null) {
296214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        // it's ok to cancel a callback that has already been called, the downloader
297214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        // will just ignore the operation.
298214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mDownloader.cancelDownload(mCallback);
299214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mCallback = null;
300214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
301214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (mIcon != null) {
302214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mMonitorListener.onMetadataChanged(mTitle, mSubtitle, mIcon,
303214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                color, appName);
304214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    } else if (mIconUri != null) {
305214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mCallback = new BitmapCallback(mSeq);
306214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mDownloader.getBitmap(
307214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                new BitmapWorkerOptions.Builder(mContext)
308214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                        .resource(mIconUri).width(mIconSize)
309214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                        .height(mIconSize).build(), mCallback);
310214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    } else {
311214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mMonitorListener.onMetadataChanged(mTitle, mSubtitle, mIcon,
312214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                                color, appName);
313214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
314214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    // Only set mCurrentMetadata after we have updated the listener (if the
315214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    // bitmap is downloaded asynchronously, that is fine too. The stream card will
316214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    // be posted, when image is downloaded.)
317214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    mCurrentMetadata = metadata;
318214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    break;
319214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
320214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                case MSG_IMAGE_DOWNLOADED:
321214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (Log.isLoggable(TAG, Log.DEBUG)) {
322214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        Log.d(TAG, "Image downloaded...");
323214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
324214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    int seq = msg.arg1;
325214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    Bitmap bitmap = (Bitmap) msg.obj;
326214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (seq == mSeq) {
327214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        mMonitorListener.onMetadataChanged(mTitle, mSubtitle, bitmap, color, appName);
328214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
329214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    break;
330214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer
331214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                case MSG_NEW_ALBUM_ART_RECEIVED:
332214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    if (Log.isLoggable(TAG, Log.DEBUG)) {
333214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                        Log.d(TAG, "Received a new album art...");
334214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    }
335214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    Bitmap newAlbumArt = (Bitmap) msg.obj;
336214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    mMonitorListener.onAlbumArtUpdated(newAlbumArt);
337214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                    break;
338214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer                default:
339214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer            }
340214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer        }
341214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer    }
342214c10ceef4ba736d8a7b3cbef06c27826822946Rakesh Iyer}
343