QueryBrowserActivity.java revision d57d69855619bd57ef2205e1c5089fa8e11b6cb1
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.ListActivity;
20import android.app.SearchManager;
21import android.content.AsyncQueryHandler;
22import android.content.BroadcastReceiver;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27
28import android.database.Cursor;
29import android.database.DatabaseUtils;
30import android.media.AudioManager;
31import android.media.MediaFile;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.Message;
36import android.provider.MediaStore;
37import android.text.TextUtils;
38import android.util.Log;
39import android.view.KeyEvent;
40import android.view.MenuItem;
41import android.view.View;
42import android.view.ViewGroup;
43import android.view.Window;
44import android.view.ViewGroup.OnHierarchyChangeListener;
45import android.widget.ImageView;
46import android.widget.ListView;
47import android.widget.SimpleCursorAdapter;
48import android.widget.TextView;
49
50import java.util.ArrayList;
51
52public class QueryBrowserActivity extends ListActivity implements MusicUtils.Defs
53{
54    private final static int PLAY_NOW = 0;
55    private final static int ADD_TO_QUEUE = 1;
56    private final static int PLAY_NEXT = 2;
57    private final static int PLAY_ARTIST = 3;
58    private final static int EXPLORE_ARTIST = 4;
59    private final static int PLAY_ALBUM = 5;
60    private final static int EXPLORE_ALBUM = 6;
61    private final static int REQUERY = 3;
62    private QueryListAdapter mAdapter;
63    private boolean mAdapterSent;
64    private String mFilterString = "";
65
66    public QueryBrowserActivity()
67    {
68    }
69
70    /** Called when the activity is first created. */
71    @Override
72    public void onCreate(Bundle icicle)
73    {
74        super.onCreate(icicle);
75        setVolumeControlStream(AudioManager.STREAM_MUSIC);
76        MusicUtils.bindToService(this);
77
78        if (icicle == null) {
79            Intent intent = getIntent();
80            mFilterString = intent.getStringExtra(SearchManager.QUERY);
81        }
82
83        IntentFilter f = new IntentFilter();
84        f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
85        f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
86        f.addDataScheme("file");
87        registerReceiver(mScanListener, f);
88
89        setContentView(R.layout.query_activity);
90        mTrackList = getListView();
91        mTrackList.setTextFilterEnabled(true);
92        mAdapter = (QueryListAdapter) getLastNonConfigurationInstance();
93        if (mAdapter == null) {
94            mAdapter = new QueryListAdapter(
95                    getApplication(),
96                    this,
97                    R.layout.track_list_item,
98                    null, // cursor
99                    new String[] {},
100                    new int[] {});
101            setListAdapter(mAdapter);
102            if (TextUtils.isEmpty(mFilterString)) {
103                getQueryCursor(mAdapter.getQueryHandler(), null);
104            } else {
105                mTrackList.setFilterText(mFilterString);
106                mFilterString = null;
107            }
108        } else {
109            mAdapter.setActivity(this);
110            setListAdapter(mAdapter);
111            mQueryCursor = mAdapter.getCursor();
112            if (mQueryCursor != null) {
113                init(mQueryCursor);
114            } else {
115                getQueryCursor(mAdapter.getQueryHandler(), mFilterString);
116            }
117        }
118    }
119
120    @Override
121    public Object onRetainNonConfigurationInstance() {
122        mAdapterSent = true;
123        return mAdapter;
124    }
125
126    @Override
127    public void onPause() {
128        mReScanHandler.removeCallbacksAndMessages(null);
129        super.onPause();
130    }
131
132    @Override
133    public void onDestroy() {
134        MusicUtils.unbindFromService(this);
135        unregisterReceiver(mScanListener);
136        super.onDestroy();
137        if (!mAdapterSent) {
138            Cursor c = mAdapter.getCursor();
139            if (c != null) {
140                c.close();
141            }
142        }
143    }
144
145    /*
146     * This listener gets called when the media scanner starts up, and when the
147     * sd card is unmounted.
148     */
149    private BroadcastReceiver mScanListener = new BroadcastReceiver() {
150        @Override
151        public void onReceive(Context context, Intent intent) {
152            MusicUtils.setSpinnerState(QueryBrowserActivity.this);
153            mReScanHandler.sendEmptyMessage(0);
154        }
155    };
156
157    private Handler mReScanHandler = new Handler() {
158        @Override
159        public void handleMessage(Message msg) {
160            getQueryCursor(mAdapter.getQueryHandler(), null);
161            // if the query results in a null cursor, onQueryComplete() will
162            // call init(), which will post a delayed message to this handler
163            // in order to try again.
164        }
165    };
166
167    @Override
168    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
169        switch (requestCode) {
170            case SCAN_DONE:
171                if (resultCode == RESULT_CANCELED) {
172                    finish();
173                } else {
174                    getQueryCursor(mAdapter.getQueryHandler(), null);
175                }
176                break;
177        }
178    }
179
180    public void init(Cursor c) {
181
182        mAdapter.changeCursor(c);
183
184        if (mQueryCursor == null) {
185            MusicUtils.displayDatabaseError(this);
186            setListAdapter(null);
187            mReScanHandler.sendEmptyMessageDelayed(0, 1000);
188            return;
189        }
190        MusicUtils.hideDatabaseError(this);
191    }
192
193    @Override
194    protected void onListItemClick(ListView l, View v, int position, long id)
195    {
196        // Dialog doesn't allow us to wait for a result, so we need to store
197        // the info we need for when the dialog posts its result
198        mQueryCursor.moveToPosition(position);
199        if (mQueryCursor.isBeforeFirst() || mQueryCursor.isAfterLast()) {
200            return;
201        }
202        String selectedType = mQueryCursor.getString(mQueryCursor.getColumnIndexOrThrow(
203                MediaStore.Audio.Media.MIME_TYPE));
204
205        if ("artist".equals(selectedType)) {
206            Intent intent = new Intent(Intent.ACTION_PICK);
207            intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/album");
208            intent.putExtra("artist", Long.valueOf(id).toString());
209            startActivity(intent);
210        } else if ("album".equals(selectedType)) {
211            Intent intent = new Intent(Intent.ACTION_PICK);
212            intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
213            intent.putExtra("album", Long.valueOf(id).toString());
214            startActivity(intent);
215        } else if (position >= 0 && id >= 0){
216            int [] list = new int[] { (int) id };
217            MusicUtils.playAll(this, list, 0);
218        } else {
219            Log.e("QueryBrowser", "invalid position/id: " + position + "/" + id);
220        }
221    }
222
223    @Override
224    public boolean onOptionsItemSelected(MenuItem item) {
225        switch (item.getItemId()) {
226            case USE_AS_RINGTONE: {
227                // Set the system setting to make this the current ringtone
228                MusicUtils.setRingtone(this, mTrackList.getSelectedItemId());
229                return true;
230            }
231
232        }
233        return super.onOptionsItemSelected(item);
234    }
235
236    private Cursor getQueryCursor(AsyncQueryHandler async, String filter) {
237        if (filter == null) {
238            filter = "";
239        }
240        String[] ccols = new String[] {
241                "_id",   // this will be the artist, album or track ID
242                MediaStore.Audio.Media.MIME_TYPE, // mimetype of audio file, or "artist" or "album"
243                SearchManager.SUGGEST_COLUMN_TEXT_1,
244                "data1",
245                "data2"
246        };
247
248        Uri search = Uri.parse("content://media/external/audio/" +
249                SearchManager.SUGGEST_URI_PATH_QUERY + "/" + Uri.encode(filter));
250
251        Cursor ret = null;
252        if (async != null) {
253            async.startQuery(0, null, search, ccols, null, null, null);
254        } else {
255            ret = MusicUtils.query(this, search, ccols, null, null, null);
256        }
257        return ret;
258    }
259
260    static class QueryListAdapter extends SimpleCursorAdapter {
261        private QueryBrowserActivity mActivity = null;
262        private AsyncQueryHandler mQueryHandler;
263
264        class QueryHandler extends AsyncQueryHandler {
265            QueryHandler(ContentResolver res) {
266                super(res);
267            }
268
269            @Override
270            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
271                mActivity.init(cursor);
272            }
273        }
274
275        QueryListAdapter(Context context, QueryBrowserActivity currentactivity,
276                int layout, Cursor cursor, String[] from, int[] to) {
277            super(context, layout, cursor, from, to);
278            mActivity = currentactivity;
279            mQueryHandler = new QueryHandler(context.getContentResolver());
280        }
281
282        public void setActivity(QueryBrowserActivity newactivity) {
283            mActivity = newactivity;
284        }
285
286        public AsyncQueryHandler getQueryHandler() {
287            return mQueryHandler;
288        }
289
290        @Override
291        public void bindView(View view, Context context, Cursor cursor) {
292
293            TextView tv1 = (TextView) view.findViewById(R.id.line1);
294            TextView tv2 = (TextView) view.findViewById(R.id.line2);
295            ImageView iv = (ImageView) view.findViewById(R.id.icon);
296            ViewGroup.LayoutParams p = iv.getLayoutParams();
297            if (p == null) {
298                // seen this happen, not sure why
299                DatabaseUtils.dumpCursor(cursor);
300                return;
301            }
302            p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
303            p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
304
305            String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(
306                    MediaStore.Audio.Media.MIME_TYPE));
307
308            if (mimetype == null) {
309                mimetype = "audio/";
310            }
311            if (mimetype.equals("artist")) {
312                iv.setImageResource(R.drawable.ic_search_category_music_artist);
313                String name = cursor.getString(cursor.getColumnIndexOrThrow(
314                        SearchManager.SUGGEST_COLUMN_TEXT_1));
315                String displayname = name;
316                if (name.equals(MediaFile.UNKNOWN_STRING)) {
317                    displayname = context.getString(R.string.unknown_artist_name);
318                }
319                tv1.setText(displayname);
320
321                int numalbums = cursor.getInt(cursor.getColumnIndexOrThrow("data1"));
322                int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow("data2"));
323
324                String songs_albums = MusicUtils.makeAlbumsSongsLabel(context,
325                        numalbums, numsongs, name.equals(MediaFile.UNKNOWN_STRING));
326
327                tv2.setText(songs_albums);
328
329            } else if (mimetype.equals("album")) {
330                iv.setImageResource(R.drawable.ic_search_category_music_album);
331                String name = cursor.getString(cursor.getColumnIndexOrThrow(
332                        SearchManager.SUGGEST_COLUMN_TEXT_1));
333                String displayname = name;
334                if (name.equals(MediaFile.UNKNOWN_STRING)) {
335                    displayname = context.getString(R.string.unknown_album_name);
336                }
337                tv1.setText(displayname);
338
339                name = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
340                displayname = name;
341                if (name.equals(MediaFile.UNKNOWN_STRING)) {
342                    displayname = context.getString(R.string.unknown_artist_name);
343                }
344                tv2.setText(displayname);
345
346            } else if(mimetype.startsWith("audio/") ||
347                    mimetype.equals("application/ogg") ||
348                    mimetype.equals("application/x-ogg")) {
349                iv.setImageResource(R.drawable.ic_search_category_music_song);
350                String name = cursor.getString(cursor.getColumnIndexOrThrow(
351                        SearchManager.SUGGEST_COLUMN_TEXT_1));
352                tv1.setText(name);
353
354                String displayname = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
355                if (name.equals(MediaFile.UNKNOWN_STRING)) {
356                    displayname = context.getString(R.string.unknown_artist_name);
357                }
358                name = cursor.getString(cursor.getColumnIndexOrThrow("data2"));
359                if (name.equals(MediaFile.UNKNOWN_STRING)) {
360                    name = context.getString(R.string.unknown_artist_name);
361                }
362                tv2.setText(displayname + " - " + name);
363            }
364        }
365        @Override
366        public void changeCursor(Cursor cursor) {
367            if (cursor != mActivity.mQueryCursor) {
368                mActivity.mQueryCursor = cursor;
369                super.changeCursor(cursor);
370            }
371        }
372        @Override
373        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
374            return mActivity.getQueryCursor(null, constraint.toString());
375        }
376    }
377
378    private ListView mTrackList;
379    private Cursor mQueryCursor;
380}
381
382