2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.music;
19 import android.app.ListActivity;
20 import android.app.SearchManager;
21 import android.content.AsyncQueryHandler;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
28 import android.database.Cursor;
29 import android.database.DatabaseUtils;
30 import android.media.AudioManager;
31 import android.media.MediaFile;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.Message;
36 import android.provider.MediaStore;
37 import android.text.TextUtils;
38 import android.util.Log;
39 import android.view.KeyEvent;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.view.Window;
44 import android.view.ViewGroup.OnHierarchyChangeListener;
45 import android.widget.ImageView;
46 import android.widget.ListView;
47 import android.widget.SimpleCursorAdapter;
48 import android.widget.TextView;
50 import java.util.ArrayList;
52 public class QueryBrowserActivity extends ListActivity implements MusicUtils.Defs
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 = "";
66 public QueryBrowserActivity()
70 /** Called when the activity is first created. */
72 public void onCreate(Bundle icicle)
74 super.onCreate(icicle);
75 setVolumeControlStream(AudioManager.STREAM_MUSIC);
76 MusicUtils.bindToService(this);
79 Intent intent = getIntent();
80 mFilterString = intent.getStringExtra(SearchManager.QUERY);
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);
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(
97 R.layout.track_list_item,
101 setListAdapter(mAdapter);
102 getQueryCursor(mAdapter.getQueryHandler(), mFilterString);
104 mAdapter.setActivity(this);
105 setListAdapter(mAdapter);
106 mQueryCursor = mAdapter.getCursor();
107 if (mQueryCursor != null) {
110 getQueryCursor(mAdapter.getQueryHandler(), mFilterString);
116 public Object onRetainNonConfigurationInstance() {
122 public void onPause() {
123 mReScanHandler.removeCallbacksAndMessages(null);
128 public void onDestroy() {
129 MusicUtils.unbindFromService(this);
130 unregisterReceiver(mScanListener);
133 Cursor c = mAdapter.getCursor();
141 * This listener gets called when the media scanner starts up, and when the
142 * sd card is unmounted.
144 private BroadcastReceiver mScanListener = new BroadcastReceiver() {
146 public void onReceive(Context context, Intent intent) {
147 MusicUtils.setSpinnerState(QueryBrowserActivity.this);
148 mReScanHandler.sendEmptyMessage(0);
152 private Handler mReScanHandler = new Handler() {
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.
163 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
164 switch (requestCode) {
166 if (resultCode == RESULT_CANCELED) {
169 getQueryCursor(mAdapter.getQueryHandler(), null);
175 public void init(Cursor c) {
177 mAdapter.changeCursor(c);
179 if (mQueryCursor == null) {
180 MusicUtils.displayDatabaseError(this);
181 setListAdapter(null);
182 mReScanHandler.sendEmptyMessageDelayed(0, 1000);
185 MusicUtils.hideDatabaseError(this);
187 if (!TextUtils.isEmpty(mFilterString)) {
188 mTrackList.setFilterText(mFilterString);
189 mFilterString = null;
194 protected void onListItemClick(ListView l, View v, int position, long id)
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()) {
202 String selectedType = mQueryCursor.getString(mQueryCursor.getColumnIndexOrThrow(
203 MediaStore.Audio.Media.MIME_TYPE));
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);
219 Log.e("QueryBrowser", "invalid position/id: " + position + "/" + id);
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());
233 return super.onOptionsItemSelected(item);
236 private Cursor getQueryCursor(AsyncQueryHandler async, String filter) {
237 if (filter == null) {
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,
248 Uri search = Uri.parse("content://media/external/audio/" +
249 SearchManager.SUGGEST_URI_PATH_QUERY + "/" + Uri.encode(filter));
253 async.startQuery(0, null, search, ccols, null, null, null);
255 ret = MusicUtils.query(this, search, ccols, null, null, null);
260 static class QueryListAdapter extends SimpleCursorAdapter {
261 private QueryBrowserActivity mActivity = null;
262 private AsyncQueryHandler mQueryHandler;
264 class QueryHandler extends AsyncQueryHandler {
265 QueryHandler(ContentResolver res) {
270 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
271 mActivity.init(cursor);
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());
282 public void setActivity(QueryBrowserActivity newactivity) {
283 mActivity = newactivity;
286 public AsyncQueryHandler getQueryHandler() {
287 return mQueryHandler;
291 public void bindView(View view, Context context, Cursor cursor) {
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();
298 // seen this happen, not sure why
299 DatabaseUtils.dumpCursor(cursor);
302 p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
303 p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
305 String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(
306 MediaStore.Audio.Media.MIME_TYPE));
308 if (mimetype == null) {
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);
319 tv1.setText(displayname);
321 int numalbums = cursor.getInt(cursor.getColumnIndexOrThrow("data1"));
322 int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow("data2"));
324 String songs_albums = MusicUtils.makeAlbumsSongsLabel(context,
325 numalbums, numsongs, name.equals(MediaFile.UNKNOWN_STRING));
327 tv2.setText(songs_albums);
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);
337 tv1.setText(displayname);
339 name = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
341 if (name.equals(MediaFile.UNKNOWN_STRING)) {
342 displayname = context.getString(R.string.unknown_artist_name);
344 tv2.setText(displayname);
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));
354 String displayname = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
355 if (name.equals(MediaFile.UNKNOWN_STRING)) {
356 displayname = context.getString(R.string.unknown_artist_name);
358 name = cursor.getString(cursor.getColumnIndexOrThrow("data2"));
359 if (name.equals(MediaFile.UNKNOWN_STRING)) {
360 name = context.getString(R.string.unknown_artist_name);
362 tv2.setText(displayname + " - " + name);
366 public void changeCursor(Cursor cursor) {
367 if (cursor != mActivity.mQueryCursor) {
368 mActivity.mQueryCursor = cursor;
369 super.changeCursor(cursor);
373 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
374 return mActivity.getQueryCursor(null, constraint.toString());
378 private ListView mTrackList;
379 private Cursor mQueryCursor;