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