QueryBrowserActivity.java revision a857e7ab7608a85c8d66e971e5807051bdb6daba
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.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import com.android.internal.database.ArrayListCursor;
26import com.android.music.TrackBrowserActivity.MyQueryHandler;
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.provider.MediaStore;
35import android.util.Log;
36import android.view.KeyEvent;
37import android.view.MenuItem;
38import android.view.View;
39import android.view.ViewGroup;
40import android.view.Window;
41import android.view.ViewGroup.OnHierarchyChangeListener;
42import android.widget.ImageView;
43import android.widget.ListView;
44import android.widget.SimpleCursorAdapter;
45import android.widget.TextView;
46
47import java.util.ArrayList;
48
49public class QueryBrowserActivity extends ListActivity implements MusicUtils.Defs
50{
51    private final static int PLAY_NOW = 0;
52    private final static int ADD_TO_QUEUE = 1;
53    private final static int PLAY_NEXT = 2;
54    private final static int PLAY_ARTIST = 3;
55    private final static int EXPLORE_ARTIST = 4;
56    private final static int PLAY_ALBUM = 5;
57    private final static int EXPLORE_ALBUM = 6;
58    private final static int REQUERY = 3;
59    private MyQueryHandler mQueryHandler;
60    private String mFilterString = "";
61
62    public QueryBrowserActivity()
63    {
64    }
65
66    /** Called when the activity is first created. */
67    @Override
68    public void onCreate(Bundle icicle)
69    {
70        super.onCreate(icicle);
71        setVolumeControlStream(AudioManager.STREAM_MUSIC);
72        MusicUtils.bindToService(this);
73
74        if (icicle == null) {
75            Intent intent = getIntent();
76            mFilterString = intent.getStringExtra(SearchManager.QUERY);
77        }
78        if (mFilterString == null) {
79            mFilterString = "";
80        }
81
82        setContentView(R.layout.query_activity);
83        mTrackList = (ListView) findViewById(android.R.id.list);
84
85        mQueryHandler = new MyQueryHandler(getContentResolver());
86        mQueryCursor = (Cursor) getLastNonConfigurationInstance();
87        if (mQueryCursor == null) {
88            //Log.i("@@@", "starting query");
89            setTitle(R.string.working_songs);
90            getQueryCursor(mQueryHandler);
91        } else {
92            init(mQueryCursor);
93        }
94    }
95
96    @Override
97    public Object onRetainNonConfigurationInstance() {
98        Cursor c = mQueryCursor;
99        mQueryCursor = null;
100        return c;
101    }
102
103    class MyQueryHandler extends AsyncQueryHandler {
104        MyQueryHandler(ContentResolver res) {
105            super(res);
106        }
107
108        @Override
109        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
110            Log.i("@@@", "query complete");
111            init(cursor);
112        }
113    }
114
115    @Override
116    public void onDestroy() {
117        MusicUtils.unbindFromService(this);
118        super.onDestroy();
119        if (mQueryCursor != null) {
120            mQueryCursor.close();
121        }
122    }
123
124    public void init(Cursor c) {
125
126        mQueryCursor = c;
127
128        ListView lv = getListView();
129        lv.setTextFilterEnabled(true);
130
131        if (mQueryCursor == null) {
132            MusicUtils.displayDatabaseError(this);
133            setListAdapter(null);
134            lv.setFocusable(false);
135            return;
136        }
137
138        // Map Cursor columns to views defined in media_list_item.xml
139        QueryListAdapter adapter = new QueryListAdapter(
140                this,
141                R.layout.track_list_item,
142                mQueryCursor,
143                new String[] {},
144                new int[] {});
145
146        setListAdapter(adapter);
147
148        if (mFilterString.length() != 0) {
149            // Hack. Can be removed once ListView supports starting filtering with a given string
150            lv.setOnHierarchyChangeListener(mHierarchyListener);
151        }
152    }
153
154    OnHierarchyChangeListener mHierarchyListener = new OnHierarchyChangeListener() {
155        public void onChildViewAdded(View parent, View child) {
156            ((ListView)parent).setOnHierarchyChangeListener(null);
157            // need to do this here to be sure all the views have been initialized
158            startFilteringWithString(mFilterString);
159        }
160
161        public void onChildViewRemoved(View parent, View child) {
162        }
163    };
164
165    private KeyEvent eventForChar(char c) {
166        int code = -1;
167        if (c >= 'a' && c <= 'z') {
168            code = KeyEvent.KEYCODE_A + (c - 'a');
169        } else if (c >= 'A' && c <= 'Z') {
170            code = KeyEvent.KEYCODE_A + (c - 'A');
171        } else if (c >= '0' && c <= '9') {
172            code = KeyEvent.KEYCODE_0 + (c - '0');
173        }
174        if (code != -1) {
175            return new KeyEvent(KeyEvent.ACTION_DOWN, code);
176        }
177        return null;
178    }
179    private void startFilteringWithString(String filterstring) {
180        ListView lv = getListView();
181        for (int i = 0; i < filterstring.length(); i++) {
182            KeyEvent event = eventForChar(filterstring.charAt(i));
183            if (event != null) {
184                lv.onKeyDown(event.getKeyCode(), event);
185            }
186        }
187    }
188
189    @Override
190    protected void onListItemClick(ListView l, View v, int position, long id)
191    {
192        // Dialog doesn't allow us to wait for a result, so we need to store
193        // the info we need for when the dialog posts its result
194        mQueryCursor.moveToPosition(position);
195        if (mQueryCursor.isBeforeFirst() || mQueryCursor.isAfterLast()) {
196            return;
197        }
198        String selectedType = mQueryCursor.getString(mQueryCursor.getColumnIndexOrThrow(
199                MediaStore.Audio.Media.MIME_TYPE));
200
201        if ("artist".equals(selectedType)) {
202            Intent intent = new Intent(Intent.ACTION_PICK);
203            intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/album");
204            intent.putExtra("artist", Long.valueOf(id).toString());
205            startActivity(intent);
206        } else if ("album".equals(selectedType)) {
207            Intent intent = new Intent(Intent.ACTION_PICK);
208            intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
209            intent.putExtra("album", Long.valueOf(id).toString());
210            startActivity(intent);
211        } else if (position >= 0 && id >= 0){
212            int [] list = new int[] { (int) id };
213            MusicUtils.playAll(this, list, 0);
214        } else {
215            Log.e("QueryBrowser", "invalid position/id: " + position + "/" + id);
216        }
217    }
218
219    @Override
220    public boolean onOptionsItemSelected(MenuItem item) {
221        switch (item.getItemId()) {
222            case USE_AS_RINGTONE: {
223                // Set the system setting to make this the current ringtone
224                MusicUtils.setRingtone(this, mTrackList.getSelectedItemId());
225                return true;
226            }
227
228        }
229        return super.onOptionsItemSelected(item);
230    }
231
232    private Cursor getQueryCursor(AsyncQueryHandler async) {
233        String[] ccols = new String[] {
234                "_id",   // this will be the artist, album or track ID
235                MediaStore.Audio.Media.MIME_TYPE, // mimetype of audio file, or "artist" or "album"
236                SearchManager.SUGGEST_COLUMN_TEXT_1,
237                "data1",
238                "data2"
239        };
240
241        Uri search = Uri.parse("content://media/external/audio/" +
242                SearchManager.SUGGEST_URI_PATH_QUERY + "/" + Uri.encode(mFilterString));
243
244        Cursor ret = null;
245        if (async != null) {
246            async.startQuery(0, null, search, ccols, null, null, null);
247        } else {
248            ret = MusicUtils.query(this, search, ccols, null, null, null);
249        }
250        return ret;
251    }
252
253    class QueryListAdapter extends SimpleCursorAdapter {
254        QueryListAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
255            super(context, layout, cursor, from, to);
256        }
257
258        @Override
259        public void bindView(View view, Context context, Cursor cursor) {
260
261            TextView tv1 = (TextView) view.findViewById(R.id.line1);
262            TextView tv2 = (TextView) view.findViewById(R.id.line2);
263            ImageView iv = (ImageView) view.findViewById(R.id.icon);
264            ViewGroup.LayoutParams p = iv.getLayoutParams();
265            if (p == null) {
266                // seen this happen, not sure why
267                DatabaseUtils.dumpCursor(cursor);
268                return;
269            }
270            p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
271            p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
272
273            String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(
274                    MediaStore.Audio.Media.MIME_TYPE));
275
276            if (mimetype == null) {
277                mimetype = "audio/";
278            }
279            if (mimetype.equals("artist")) {
280                iv.setImageResource(R.drawable.ic_search_category_music_artist);
281                String name = cursor.getString(cursor.getColumnIndexOrThrow(
282                        SearchManager.SUGGEST_COLUMN_TEXT_1));
283                String displayname = name;
284                if (name.equals(MediaFile.UNKNOWN_STRING)) {
285                    displayname = context.getString(R.string.unknown_artist_name);
286                }
287                tv1.setText(displayname);
288
289                int numalbums = cursor.getInt(cursor.getColumnIndexOrThrow("data1"));
290                int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow("data2"));
291
292                String songs_albums = MusicUtils.makeAlbumsSongsLabel(context,
293                        numalbums, numsongs, name.equals(MediaFile.UNKNOWN_STRING));
294
295                tv2.setText(songs_albums);
296
297            } else if (mimetype.equals("album")) {
298                iv.setImageResource(R.drawable.ic_search_category_music_album);
299                String name = cursor.getString(cursor.getColumnIndexOrThrow(
300                        SearchManager.SUGGEST_COLUMN_TEXT_1));
301                String displayname = name;
302                if (name.equals(MediaFile.UNKNOWN_STRING)) {
303                    displayname = context.getString(R.string.unknown_album_name);
304                }
305                tv1.setText(displayname);
306
307                name = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
308                displayname = name;
309                if (name.equals(MediaFile.UNKNOWN_STRING)) {
310                    displayname = context.getString(R.string.unknown_artist_name);
311                }
312                tv2.setText(displayname);
313
314            } else if(mimetype.startsWith("audio/") ||
315                    mimetype.equals("application/ogg") ||
316                    mimetype.equals("application/x-ogg")) {
317                iv.setImageResource(R.drawable.ic_search_category_music_song);
318                String name = cursor.getString(cursor.getColumnIndexOrThrow(
319                        SearchManager.SUGGEST_COLUMN_TEXT_1));
320                tv1.setText(name);
321
322                String displayname = cursor.getString(cursor.getColumnIndexOrThrow("data1"));
323                if (name.equals(MediaFile.UNKNOWN_STRING)) {
324                    displayname = context.getString(R.string.unknown_artist_name);
325                }
326                name = cursor.getString(cursor.getColumnIndexOrThrow("data2"));
327                if (name.equals(MediaFile.UNKNOWN_STRING)) {
328                    name = context.getString(R.string.unknown_artist_name);
329                }
330                tv2.setText(displayname + " - " + name);
331            }
332        }
333        @Override
334        public void changeCursor(Cursor cursor) {
335            super.changeCursor(cursor);
336            mQueryCursor = cursor;
337        }
338        @Override
339        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
340            mFilterString = constraint.toString();
341            return getQueryCursor(null);
342        }
343    }
344
345    private ListView mTrackList;
346    private Cursor mQueryCursor;
347}
348
349