PlaylistBrowserActivity.java revision 6cb8bc92e0ca524a76a6fa3f6814b43ea9a3b30d
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 java.text.Collator;
20import java.util.ArrayList;
21
22import android.app.ListActivity;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.ServiceConnection;
29
30import com.android.internal.database.ArrayListCursor;
31import android.database.Cursor;
32import android.database.MergeCursor;
33import android.media.AudioManager;
34import android.net.Uri;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.IBinder;
38import android.os.Message;
39import android.provider.MediaStore;
40import android.util.Log;
41import android.view.ContextMenu;
42import android.view.Menu;
43import android.view.MenuItem;
44import android.view.View;
45import android.view.ViewGroup;
46import android.view.Window;
47import android.view.ContextMenu.ContextMenuInfo;
48import android.widget.ImageView;
49import android.widget.ListView;
50import android.widget.SimpleCursorAdapter;
51import android.widget.TextView;
52import android.widget.Toast;
53import android.widget.AdapterView.AdapterContextMenuInfo;
54
55public class PlaylistBrowserActivity extends ListActivity
56    implements View.OnCreateContextMenuListener, MusicUtils.Defs
57{
58    private static final int DELETE_PLAYLIST = CHILD_MENU_BASE + 1;
59    private static final int EDIT_PLAYLIST = CHILD_MENU_BASE + 2;
60    private static final int RENAME_PLAYLIST = CHILD_MENU_BASE + 3;
61    private static final int CHANGE_WEEKS = CHILD_MENU_BASE + 4;
62    private static final long RECENTLY_ADDED_PLAYLIST = -1;
63    private static final long ALL_SONGS_PLAYLIST = -2;
64
65    private boolean mCreateShortcut;
66
67    public PlaylistBrowserActivity()
68    {
69    }
70
71    /** Called when the activity is first created. */
72    @Override
73    public void onCreate(Bundle icicle)
74    {
75        super.onCreate(icicle);
76
77        final Intent intent = getIntent();
78        final String action = intent.getAction();
79        if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
80            mCreateShortcut = true;
81        }
82
83        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
84        setVolumeControlStream(AudioManager.STREAM_MUSIC);
85        MusicUtils.bindToService(this, new ServiceConnection() {
86            public void onServiceConnected(ComponentName classname, IBinder obj) {
87                if (Intent.ACTION_VIEW.equals(action)) {
88                    long id = Long.parseLong(intent.getExtras().getString("playlist"));
89                    if (id == RECENTLY_ADDED_PLAYLIST) {
90                        playRecentlyAdded();
91                    } else if (id == ALL_SONGS_PLAYLIST) {
92                        int [] list = MusicUtils.getAllSongs(PlaylistBrowserActivity.this);
93                        if (list != null) {
94                            MusicUtils.playAll(PlaylistBrowserActivity.this, list, 0);
95                        }
96                    } else {
97                        MusicUtils.playPlaylist(PlaylistBrowserActivity.this, id);
98                    }
99                    finish();
100                }
101            }
102
103            public void onServiceDisconnected(ComponentName classname) {
104            }
105
106        });
107        IntentFilter f = new IntentFilter();
108        f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
109        f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
110        f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
111        f.addDataScheme("file");
112        registerReceiver(mScanListener, f);
113
114        init();
115    }
116
117
118
119    @Override
120    public void onDestroy() {
121        MusicUtils.unbindFromService(this);
122        if (mPlaylistCursor != null) {
123            mPlaylistCursor.close();
124        }
125        unregisterReceiver(mScanListener);
126        super.onDestroy();
127    }
128
129    @Override
130    public void onResume() {
131        super.onResume();
132
133        MusicUtils.setSpinnerState(this);
134    }
135    @Override
136    public void onPause() {
137        mReScanHandler.removeCallbacksAndMessages(null);
138        super.onPause();
139    }
140    private BroadcastReceiver mScanListener = new BroadcastReceiver() {
141        @Override
142        public void onReceive(Context context, Intent intent) {
143            MusicUtils.setSpinnerState(PlaylistBrowserActivity.this);
144            mReScanHandler.sendEmptyMessage(0);
145        }
146    };
147
148    private Handler mReScanHandler = new Handler() {
149        public void handleMessage(Message msg) {
150            init();
151            if (mPlaylistCursor == null) {
152                sendEmptyMessageDelayed(0, 1000);
153            }
154        }
155    };
156    public void init() {
157
158        mPlaylistCursor = getPlaylistCursor(null);
159
160        if (mPlaylistCursor == null) {
161            MusicUtils.displayDatabaseError(this);
162            setContentView(R.layout.no_sd_card);
163            return;
164        }
165
166        setContentView(R.layout.media_picker_activity);
167
168        if (mPlaylistCursor.getCount() > 0) {
169            mPlaylistCursor.moveToFirst();
170
171            setTitle(R.string.playlists_title);
172        } else {
173            setTitle(R.string.no_playlists_title);
174        }
175
176        // Map Cursor columns to views defined in media_list_item.xml
177        PlaylistListAdapter adapter = new PlaylistListAdapter(
178                this,
179                R.layout.track_list_item,
180                mPlaylistCursor,
181                new String[] { MediaStore.Audio.Playlists.NAME},
182                new int[] { android.R.id.text1 });
183
184        setListAdapter(adapter);
185        ListView lv = getListView();
186        lv.setOnCreateContextMenuListener(this);
187        lv.setTextFilterEnabled(true);
188    }
189
190    @Override
191    public boolean onCreateOptionsMenu(Menu menu) {
192        if (!mCreateShortcut) {
193            menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(
194                    R.drawable.ic_menu_music_library);
195            menu.add(0, GOTO_PLAYBACK, 0, R.string.goto_playback).setIcon(
196                    R.drawable.ic_menu_playback).setVisible(MusicUtils.isMusicLoaded());
197        }
198        return super.onCreateOptionsMenu(menu);
199    }
200
201    @Override
202    public boolean onOptionsItemSelected(MenuItem item) {
203        Intent intent;
204        switch (item.getItemId()) {
205            case GOTO_START:
206                intent = new Intent();
207                intent.setClass(this, MusicBrowserActivity.class);
208                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
209                startActivity(intent);
210                return true;
211
212            case GOTO_PLAYBACK:
213                intent = new Intent("com.android.music.PLAYBACK_VIEWER");
214                startActivity(intent);
215                return true;
216        }
217        return super.onOptionsItemSelected(item);
218    }
219
220    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
221        if (mCreateShortcut) {
222            return;
223        }
224
225        AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn;
226
227        if (mi.id < 0) {
228            menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
229            menu.add(0, EDIT_PLAYLIST, 0, R.string.edit_playlist_menu);
230            mPlaylistCursor.moveToPosition(mi.position);
231            menu.setHeaderTitle(mPlaylistCursor.getString(mPlaylistCursor.getColumnIndex(MediaStore.Audio.Playlists.NAME)));
232        } else {
233            menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
234            menu.add(0, DELETE_PLAYLIST, 0, R.string.delete_playlist_menu);
235            menu.add(0, EDIT_PLAYLIST, 0, R.string.edit_playlist_menu);
236            menu.add(0, RENAME_PLAYLIST, 0, R.string.rename_playlist_menu);
237            mPlaylistCursor.moveToPosition(mi.position);
238            menu.setHeaderTitle(mPlaylistCursor.getString(mPlaylistCursor.getColumnIndex(MediaStore.Audio.Playlists.NAME)));
239        }
240    }
241
242    @Override
243    public boolean onContextItemSelected(MenuItem item) {
244        AdapterContextMenuInfo mi = (AdapterContextMenuInfo) item.getMenuInfo();
245        switch (item.getItemId()) {
246            case PLAY_SELECTION:
247                if (mi.id == RECENTLY_ADDED_PLAYLIST) {
248                    playRecentlyAdded();
249                } else {
250                    MusicUtils.playPlaylist(this, mi.id);
251                }
252                break;
253            case DELETE_PLAYLIST:
254                mPlaylistCursor.moveToPosition(mi.position);
255                mPlaylistCursor.deleteRow();
256                mPlaylistCursor.commitUpdates();
257                Toast.makeText(this, R.string.playlist_deleted_message, Toast.LENGTH_SHORT).show();
258                if (mPlaylistCursor.getCount() == 0) {
259                    setTitle(R.string.no_playlists_title);
260                }
261                break;
262            case EDIT_PLAYLIST:
263                if (mi.id == RECENTLY_ADDED_PLAYLIST) {
264                    Intent intent = new Intent();
265                    intent.setClass(this, WeekSelector.class);
266                    startActivityForResult(intent, CHANGE_WEEKS);
267                    return true;
268                } else {
269                    Intent intent = new Intent(Intent.ACTION_EDIT);
270                    intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
271                    intent.putExtra("playlist", Long.valueOf(mi.id).toString());
272                    startActivity(intent);
273                }
274                break;
275            case RENAME_PLAYLIST:
276                Intent intent = new Intent();
277                intent.setClass(this, RenamePlaylist.class);
278                intent.putExtra("rename", mi.id);
279                startActivityForResult(intent, RENAME_PLAYLIST);
280                break;
281        }
282        return true;
283    }
284
285    @Override
286    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
287        switch (requestCode) {
288            case SCAN_DONE:
289                if (resultCode == RESULT_CANCELED) {
290                    finish();
291                } else {
292                    init();
293                }
294                break;
295        }
296    }
297
298    @Override
299    protected void onListItemClick(ListView l, View v, int position, long id)
300    {
301        if (mCreateShortcut) {
302            final Intent shortcut = new Intent();
303            shortcut.setAction(Intent.ACTION_VIEW);
304            shortcut.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/playlist");
305            shortcut.putExtra("playlist", String.valueOf(id));
306
307            final Intent intent = new Intent();
308            intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut);
309            intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, ((TextView) v.findViewById(R.id.line1)).getText());
310            intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(
311                    this, R.drawable.app_music));
312
313            setResult(RESULT_OK, intent);
314            finish();
315            return;
316        }
317        if (id == RECENTLY_ADDED_PLAYLIST) {
318            playRecentlyAdded();
319        } else {
320            MusicUtils.playPlaylist(this, id);
321        }
322    }
323
324    private void playRecentlyAdded() {
325        // do a query for all playlists in the last X weeks
326        int X = MusicUtils.getIntPref(this, "numweeks", 2) * (3600 * 24 * 7);
327        final String[] ccols = new String[] { MediaStore.Audio.Media._ID};
328        String where = MediaStore.MediaColumns.DATE_ADDED + ">" + (System.currentTimeMillis() / 1000 - X);
329        Cursor cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
330                ccols, where, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
331
332        if (cursor == null) {
333            // Todo: show a message
334            return;
335        }
336        int len = cursor.getCount();
337        int [] list = new int[len];
338        for (int i = 0; i < len; i++) {
339            cursor.moveToNext();
340            list[i] = cursor.getInt(0);
341        }
342        cursor.close();
343        MusicUtils.playAll(this, list, 0);
344    }
345
346    private Cursor getPlaylistCursor(String filterstring) {
347        String[] cols = new String[] {
348                MediaStore.Audio.Playlists._ID,
349                MediaStore.Audio.Playlists.NAME
350        };
351
352        StringBuilder where = new StringBuilder();
353        where.append(MediaStore.Audio.Playlists.NAME + " != ''");
354
355        // Add in the filtering constraints
356        String [] keywords = null;
357        if (filterstring != null) {
358            String [] searchWords = filterstring.split(" ");
359            keywords = new String[searchWords.length];
360            Collator col = Collator.getInstance();
361            col.setStrength(Collator.PRIMARY);
362            for (int i = 0; i < searchWords.length; i++) {
363                keywords[i] = '%' + searchWords[i] + '%';
364            }
365            for (int i = 0; i < searchWords.length; i++) {
366                where.append(" AND ");
367                where.append(MediaStore.Audio.Playlists.NAME + " LIKE ?");
368            }
369        }
370
371        String whereclause = where.toString();
372
373        Cursor results = MusicUtils.query(this, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
374            cols, whereclause, keywords,
375            MediaStore.Audio.Playlists.NAME);
376
377        if (results == null) {
378            return null;
379        }
380        ArrayList<ArrayList> autoplaylists = new ArrayList<ArrayList>();
381        if (mCreateShortcut) {
382            ArrayList<Object> all = new ArrayList<Object>(2);
383            all.add(ALL_SONGS_PLAYLIST);
384            all.add(getString(R.string.play_all));
385            autoplaylists.add(all);
386        }
387        ArrayList<Object> recent = new ArrayList<Object>(2);
388        recent.add(RECENTLY_ADDED_PLAYLIST);
389        recent.add(getString(R.string.recentlyadded));
390        autoplaylists.add(recent);
391
392        ArrayListCursor autoplaylistscursor = new ArrayListCursor(cols, autoplaylists);
393
394        Cursor c = new MergeCursor(new Cursor [] {autoplaylistscursor, results});
395
396        return c;
397    }
398
399    class PlaylistListAdapter extends SimpleCursorAdapter {
400        int mTitleIdx;
401
402        PlaylistListAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
403            super(context, layout, cursor, from, to);
404
405            mTitleIdx = cursor.getColumnIndex(MediaStore.Audio.Playlists.NAME);
406        }
407
408        @Override
409        public void bindView(View view, Context context, Cursor cursor) {
410
411            TextView tv = (TextView) view.findViewById(R.id.line1);
412
413            String name = cursor.getString(mTitleIdx);
414            tv.setText(name);
415
416            ImageView iv = (ImageView) view.findViewById(R.id.icon);
417            iv.setImageResource(R.drawable.ic_mp_playlist_list);
418            ViewGroup.LayoutParams p = iv.getLayoutParams();
419            p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
420            p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
421
422            iv = (ImageView) view.findViewById(R.id.play_indicator);
423            iv.setVisibility(View.GONE);
424        }
425
426        @Override
427        public void changeCursor(Cursor cursor) {
428            super.changeCursor(cursor);
429            mPlaylistCursor = cursor;
430        }
431        @Override
432        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
433            return getPlaylistCursor(constraint.toString());
434        }
435    }
436
437    private Cursor mPlaylistCursor;
438}
439
440