1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.music;
18
19import android.app.Activity;
20import android.app.ListActivity;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.graphics.Color;
25import android.graphics.drawable.Drawable;
26import android.media.AudioManager;
27import android.media.MediaDescription;
28import android.media.MediaMetadata;
29import android.media.browse.MediaBrowser;
30import android.media.session.MediaController;
31import android.media.session.MediaSession;
32import android.os.Bundle;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.view.Window;
38import android.widget.*;
39import com.android.music.utils.LogHelper;
40import com.android.music.utils.MediaIDHelper;
41
42import java.util.ArrayList;
43import java.util.List;
44
45/*
46This activity shows when there is a need for
47 1. Songs tab [withtab = true]
48 2. Browse songs within an album [withtab = false]
49 3. Browse songs within a playlist [withtab = false]
50 4. Browse songs within now playing queue [withtab = false]
51 */
52public class TrackBrowserActivity extends ListActivity {
53    private static final String TAG = LogHelper.makeLogTag(TrackBrowserActivity.class);
54    private static final MediaBrowser.MediaItem DEFAULT_PARENT_ITEM =
55            new MediaBrowser.MediaItem(new MediaDescription.Builder()
56                                               .setMediaId(MediaIDHelper.MEDIA_ID_MUSICS_BY_SONG)
57                                               .setTitle("Songs")
58                                               .build(),
59                    MediaBrowser.MediaItem.FLAG_BROWSABLE);
60
61    // Underlining ListView of this Activity
62    private ListView mTrackList;
63    // The mediaId to be used for subscribing for children using the MediaBrowser.
64    private MediaBrowser.MediaItem mParentItem;
65    private MediaBrowser mMediaBrowser;
66    private TrackBrowseAdapter mBrowseListAdapter;
67    private boolean mWithTabs;
68
69    /**
70     * Called when the activity is first created.
71     */
72    @Override
73    public void onCreate(Bundle icicle) {
74        Log.d(TAG, "onCreate()");
75        super.onCreate(icicle);
76        // Process past states
77        Intent intent = getIntent();
78        if (icicle != null) {
79            LogHelper.d(TAG, "Launch by saved instance state");
80            mParentItem = icicle.getParcelable(MusicUtils.TAG_PARENT_ITEM);
81            mWithTabs = icicle.getBoolean(MusicUtils.TAG_WITH_TABS);
82            MusicUtils.updateNowPlaying(this);
83        } else if (intent != null) {
84            LogHelper.d(TAG, "Launch by intent");
85            mParentItem = intent.getParcelableExtra(MusicUtils.TAG_PARENT_ITEM);
86            mWithTabs = intent.getBooleanExtra(MusicUtils.TAG_WITH_TABS, false);
87        }
88        if (mParentItem == null) {
89            LogHelper.d(TAG, "Launch by default parameters");
90            mParentItem = DEFAULT_PARENT_ITEM;
91            mWithTabs = true;
92        }
93        if (mWithTabs) {
94            requestWindowFeature(Window.FEATURE_NO_TITLE);
95        }
96        setTitle(mParentItem.getDescription().getTitle());
97        setVolumeControlStream(AudioManager.STREAM_MUSIC);
98
99        // Init layout
100        LogHelper.d(TAG, "init layout");
101        setContentView(R.layout.media_picker_activity);
102        MusicUtils.updateButtonBar(this, R.id.songtab);
103
104        // Init the ListView
105        Log.d(TAG, "Creating ListView");
106        mTrackList = getListView();
107        mTrackList.setCacheColorHint(0);
108        mTrackList.setTextFilterEnabled(true);
109        mBrowseListAdapter = (TrackBrowseAdapter) getLastNonConfigurationInstance();
110        if (mBrowseListAdapter == null) {
111            mBrowseListAdapter = new TrackBrowseAdapter(this, R.layout.track_list_item);
112        }
113        setListAdapter(mBrowseListAdapter);
114        // don't set the album art until after the view has been layed out
115        mTrackList.post(new Runnable() {
116            public void run() {
117                mTrackList.setBackgroundColor(Color.WHITE);
118                mTrackList.setCacheColorHint(0);
119            }
120        });
121
122        // Create media browser
123        Log.d(TAG, "Creating MediaBrowser");
124        mMediaBrowser = new MediaBrowser(this, new ComponentName(this, MediaPlaybackService.class),
125                mConnectionCallback, null);
126    }
127
128    @Override
129    public void onStart() {
130        Log.d(TAG, "onStart()");
131        super.onStart();
132        mMediaBrowser.connect();
133    }
134
135    @Override
136    public void onStop() {
137        Log.d(TAG, "onStop()");
138        super.onStop();
139        mMediaBrowser.disconnect();
140    }
141
142    @Override
143    public void onDestroy() {
144        Log.d(TAG, "onDestroy()");
145        ListView lv = getListView();
146        // Because we pass the adapter to the next activity, we need to make
147        // sure it doesn't keep a reference to this activity. We can do this
148        // by clearing its DatasetObservers, which setListAdapter(null) does.
149        setListAdapter(null);
150        mBrowseListAdapter = null;
151        super.onDestroy();
152    }
153
154    @Override
155    public void onResume() {
156        Log.d(TAG, "onResume()");
157        super.onResume();
158    }
159
160    @Override
161    public void onPause() {
162        Log.d(TAG, "onPause()");
163        super.onPause();
164    }
165
166    @Override
167    public void onSaveInstanceState(Bundle outcicle) {
168        outcicle.putParcelable(MusicUtils.TAG_PARENT_ITEM, mParentItem);
169        outcicle.putBoolean(MusicUtils.TAG_WITH_TABS, mWithTabs);
170        super.onSaveInstanceState(outcicle);
171    }
172
173    private MediaBrowser.SubscriptionCallback mSubscriptionCallback =
174            new MediaBrowser.SubscriptionCallback() {
175
176                @Override
177                public void onChildrenLoaded(
178                        String parentId, List<MediaBrowser.MediaItem> children) {
179                    mBrowseListAdapter.clear();
180                    mBrowseListAdapter.notifyDataSetInvalidated();
181                    for (MediaBrowser.MediaItem item : children) {
182                        mBrowseListAdapter.add(item);
183                    }
184                    mBrowseListAdapter.notifyDataSetChanged();
185                }
186
187                @Override
188                public void onError(String id) {
189                    Toast.makeText(getApplicationContext(), R.string.error_loading_media,
190                                 Toast.LENGTH_LONG)
191                            .show();
192                }
193            };
194
195    private MediaBrowser.ConnectionCallback mConnectionCallback =
196            new MediaBrowser.ConnectionCallback() {
197                @Override
198                public void onConnected() {
199                    Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
200                    mMediaBrowser.subscribe(mParentItem.getMediaId(), mSubscriptionCallback);
201                    if (mMediaBrowser.getSessionToken() == null) {
202                        throw new IllegalArgumentException("No Session token");
203                    }
204                    MediaController mediaController = new MediaController(
205                            TrackBrowserActivity.this, mMediaBrowser.getSessionToken());
206                    mediaController.registerCallback(mMediaControllerCallback);
207                    TrackBrowserActivity.this.setMediaController(mediaController);
208                    if (mediaController.getMetadata() != null && mWithTabs) {
209                        MusicUtils.updateNowPlaying(TrackBrowserActivity.this);
210                    }
211                }
212
213                @Override
214                public void onConnectionFailed() {
215                    Log.d(TAG, "onConnectionFailed");
216                }
217
218                @Override
219                public void onConnectionSuspended() {
220                    Log.d(TAG, "onConnectionSuspended");
221                    TrackBrowserActivity.this.setMediaController(null);
222                }
223            };
224
225    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
226        @Override
227        public void onMetadataChanged(MediaMetadata metadata) {
228            super.onMetadataChanged(metadata);
229            if (mWithTabs) {
230                MusicUtils.updateNowPlaying(TrackBrowserActivity.this);
231            }
232            if (mBrowseListAdapter != null) {
233                mBrowseListAdapter.notifyDataSetChanged();
234            }
235        }
236    };
237
238    // An adapter for showing the list of browsed MediaItem's
239    private static class TrackBrowseAdapter extends ArrayAdapter<MediaBrowser.MediaItem> {
240        private int mLayoutId;
241        private final Drawable mNowPlayingOverlay;
242        private Activity mActivity;
243
244        static class ViewHolder {
245            TextView line1;
246            TextView line2;
247            TextView duration;
248            ImageView play_indicator;
249        }
250
251        TrackBrowseAdapter(Activity activity, int layout) {
252            super(activity, layout, new ArrayList<>());
253            mLayoutId = layout;
254            mNowPlayingOverlay = activity.getResources().getDrawable(
255                    R.drawable.indicator_ic_mp_playing_list, activity.getTheme());
256            mActivity = activity;
257        }
258
259        @Override
260        public View getView(int position, View convertView, ViewGroup parent) {
261            Log.d(TAG, "getView()");
262            if (convertView == null) {
263                convertView = LayoutInflater.from(getContext()).inflate(mLayoutId, parent, false);
264                ImageView iv = (ImageView) convertView.findViewById(R.id.icon);
265                iv.setVisibility(View.GONE);
266                ViewHolder vh = new ViewHolder();
267                vh.line1 = (TextView) convertView.findViewById(R.id.line1);
268                vh.line2 = (TextView) convertView.findViewById(R.id.line2);
269                vh.duration = (TextView) convertView.findViewById(R.id.duration);
270                vh.play_indicator = (ImageView) convertView.findViewById(R.id.play_indicator);
271                convertView.setTag(vh);
272            }
273            ViewHolder holder = (ViewHolder) convertView.getTag();
274            MediaBrowser.MediaItem item = getItem(position);
275            Log.d(TAG, "title: " + item.getDescription().getTitle());
276            holder.line1.setText(item.getDescription().getTitle());
277            Log.d(TAG, "artist: " + item.getDescription().getSubtitle());
278            holder.line2.setText(item.getDescription().getSubtitle());
279            long duration =
280                    item.getDescription().getExtras().getLong(MediaMetadata.METADATA_KEY_DURATION);
281            LogHelper.d(TAG, "duration: ", duration);
282            holder.duration.setText(MusicUtils.makeTimeString(getContext(), duration / 1000));
283            MediaController mediaController = mActivity.getMediaController();
284            if (mediaController == null) {
285                holder.play_indicator.setImageDrawable(null);
286                return convertView;
287            }
288            MediaMetadata metadata = mediaController.getMetadata();
289            if (metadata == null) {
290                holder.play_indicator.setImageDrawable(null);
291                return convertView;
292            }
293            if (item.getDescription().getMediaId().endsWith(
294                        metadata.getString(MediaMetadata.METADATA_KEY_MEDIA_ID))) {
295                holder.play_indicator.setImageDrawable(mNowPlayingOverlay);
296            } else {
297                holder.play_indicator.setImageDrawable(null);
298            }
299            return convertView;
300        }
301    }
302
303    @Override
304    public Object onRetainNonConfigurationInstance() {
305        TrackBrowseAdapter a = mBrowseListAdapter;
306        return a;
307    }
308
309    @Override
310    protected void onListItemClick(ListView l, View v, int position, long id) {
311        Log.d(TAG, "onListItemClick at position " + position + ", id " + id);
312        MediaBrowser.MediaItem item = mBrowseListAdapter.getItem(position);
313        if (item.isPlayable()) {
314            getMediaController().getTransportControls().playFromMediaId(item.getMediaId(), null);
315        }
316    }
317}
318