OSDN Git Service

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