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