QueryBrowserActivity.java revision 95c42939a307b02e84fbcd4186974a7607657996
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            getQueryCursor(mAdapter.getQueryHandler(), mFilterString);
103        } else {
104            mAdapter.setActivity(this);
105            setListAdapter(mAdapter);
106            mQueryCursor = mAdapter.getCursor();
107            if (mQueryCursor != null) {
108                init(mQueryCursor);
109            } else {
110                getQueryCursor(mAdapter.getQueryHandler(), mFilterString);
111            }
112        }
113    }
114
115    @Override
116    public Object onRetainNonConfigurationInstance() {
117        mAdapterSent = true;
118        return mAdapter;
119    }
120
121    @Override
122    public void onPause() {
123        mReScanHandler.removeCallbacksAndMessages(null);
124        super.onPause();
125    }
126
127    @Override
128    public void onDestroy() {
129        MusicUtils.unbindFromService(this);
130        unregisterReceiver(mScanListener);
131        super.onDestroy();
132        if (!mAdapterSent) {
133            Cursor c = mAdapter.getCursor();
134            if (c != null) {
135                c.close();
136            }
137        }
138    }
139
140    /*
141     * This listener gets called when the media scanner starts up, and when the
142     * sd card is unmounted.
143     */
144    private BroadcastReceiver mScanListener = new BroadcastReceiver() {
145        @Override
146        public void onReceive(Context context, Intent intent) {
147            MusicUtils.setSpinnerState(QueryBrowserActivity.this);
148            mReScanHandler.sendEmptyMessage(0);
149        }
150    };
151
152    private Handler mReScanHandler = new Handler() {
153        @Override
154        public void handleMessage(Message msg) {
155            getQueryCursor(mAdapter.getQueryHandler(), null);
156            // if the query results in a null cursor, onQueryComplete() will
157            // call init(), which will post a delayed message to this handler
158            // in order to try again.
159        }
160    };
161
162    @Override
163    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
164        switch (requestCode) {
165            case SCAN_DONE:
166                if (resultCode == RESULT_CANCELED) {
167                    finish();
168                } else {
169                    getQueryCursor(mAdapter.getQueryHandler(), null);
170                }
171                break;
172        }
173    }
174
175    public void init(Cursor c) {
176
177        mAdapter.changeCursor(c);
178
179        if (mQueryCursor == null) {
180            MusicUtils.displayDatabaseError(this);
181            setListAdapter(null);
182            mReScanHandler.sendEmptyMessageDelayed(0, 1000);
183            return;
184        }
185        MusicUtils.hideDatabaseError(this);
186
187        if (!TextUtils.isEmpty(mFilterString)) {
188            mTrackList.setFilterText(mFilterString);
189            mFilterString = null;
190        }
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