OSDN Git Service

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