Java tutorial
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.music; import java.text.Collator; import java.util.ArrayList; import android.content.AsyncQueryHandler; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MergeCursor; import android.database.sqlite.SQLiteException; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import com.android.music.MusicUtils.ServiceToken; public class PlaylistBrowserFragment extends Fragment implements View.OnCreateContextMenuListener, MusicUtils.Defs { private static final String TAG = "PlaylistBrowserActivity"; private static final int DELETE_PLAYLIST = CHILD_MENU_BASE + 1; private static final int EDIT_PLAYLIST = CHILD_MENU_BASE + 2; private static final int RENAME_PLAYLIST = CHILD_MENU_BASE + 3; private static final int CHANGE_WEEKS = CHILD_MENU_BASE + 4; private static final long RECENTLY_ADDED_PLAYLIST = -1; private static final long ALL_SONGS_PLAYLIST = -2; private static final long PODCASTS_PLAYLIST = -3; private PlaylistListAdapter mAdapter; boolean mAdapterSent; private static int mLastListPosCourse = -1; private static int mLastListPosFine = -1; private boolean mCreateShortcut; private ServiceToken mToken; private ListView lv; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // TODO Auto-generated method stub View view = inflater.inflate(R.layout.media_picker_activity, null); final Intent intent = getActivity().getIntent(); final String action = intent.getAction(); if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { mCreateShortcut = true; } getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); mToken = MusicUtils.bindToService(getActivity(), new ServiceConnection() { public void onServiceConnected(ComponentName classname, IBinder obj) { if (Intent.ACTION_VIEW.equals(action)) { Bundle b = intent.getExtras(); if (b == null) { Log.w(TAG, "Unexpected:getExtras() returns null."); } else { try { long id = Long.parseLong(b.getString("playlist")); if (id == RECENTLY_ADDED_PLAYLIST) { playRecentlyAdded(); } else if (id == PODCASTS_PLAYLIST) { playPodcasts(); } else if (id == ALL_SONGS_PLAYLIST) { long[] list = MusicUtils.getAllSongs(getActivity()); if (list != null) { MusicUtils.playAll(getActivity(), list, 0); } } else { MusicUtils.playPlaylist(getActivity(), id); } } catch (NumberFormatException e) { Log.w(TAG, "Playlist id missing or broken"); } } getActivity().finish(); return; } MusicUtils.updateNowPlaying(getActivity()); } public void onServiceDisconnected(ComponentName classname) { } }); IntentFilter f = new IntentFilter(); f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); f.addAction(Intent.ACTION_MEDIA_UNMOUNTED); f.addDataScheme("file"); getActivity().registerReceiver(mScanListener, f); lv = (ListView) view.findViewById(android.R.id.list); lv.setOnCreateContextMenuListener(this); lv.setTextFilterEnabled(true); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub if (mCreateShortcut) { final Intent shortcut = new Intent(); shortcut.setAction(Intent.ACTION_VIEW); shortcut.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/playlist"); shortcut.putExtra("playlist", String.valueOf(id)); final Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut); intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, ((TextView) view.findViewById(R.id.line1)).getText()); intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource .fromContext(getActivity(), R.drawable.ic_launcher_shortcut_music_playlist)); getActivity().setResult(getActivity().RESULT_OK, intent); getActivity().finish(); return; } if (id == RECENTLY_ADDED_PLAYLIST) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); intent.putExtra("playlist", "recentlyadded"); startActivity(intent); } else if (id == PODCASTS_PLAYLIST) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); intent.putExtra("playlist", "podcasts"); startActivity(intent); } else { Intent intent = new Intent(Intent.ACTION_EDIT); intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); intent.putExtra("playlist", Long.valueOf(id).toString()); startActivity(intent); } } }); mAdapter = (PlaylistListAdapter) getActivity().getLastNonConfigurationInstance(); if (mAdapter == null) { //Log.i("@@@", "starting query"); mAdapter = new PlaylistListAdapter(getActivity().getApplication(), this, R.layout.track_list_item, mPlaylistCursor, new String[] { MediaStore.Audio.Playlists.NAME }, new int[] { android.R.id.text1 }); lv.setAdapter(mAdapter); //setTitle(R.string.working_playlists); getPlaylistCursor(mAdapter.getQueryHandler(), null); } else { mAdapter.setActivity(this); lv.setAdapter(mAdapter); mPlaylistCursor = mAdapter.getCursor(); // If mPlaylistCursor is null, this can be because it doesn't have // a cursor yet (because the initial query that sets its cursor // is still in progress), or because the query failed. // In order to not flash the error dialog at the user for the // first case, simply retry the query when the cursor is null. // Worst case, we end up doing the same query twice. if (mPlaylistCursor != null) { init(mPlaylistCursor); } else { //setTitle(R.string.working_playlists); getPlaylistCursor(mAdapter.getQueryHandler(), null); } } return view; } /* @Override public Object onRetainNonConfigurationInstance() { PlaylistListAdapter a = mAdapter; mAdapterSent = true; return a; } */ @Override public void onDestroy() { //ListView lv = getListView(); if (lv != null) { mLastListPosCourse = lv.getFirstVisiblePosition(); View cv = lv.getChildAt(0); if (cv != null) { mLastListPosFine = cv.getTop(); } } MusicUtils.unbindFromService(mToken); // If we have an adapter and didn't send it off to another activity yet, we should // close its cursor, which we do by assigning a null cursor to it. Doing this // instead of closing the cursor directly keeps the framework from accessing // the closed cursor later. if (!mAdapterSent && mAdapter != null) { mAdapter.changeCursor(null); } // Because we pass the adapter to the next activity, we need to make // sure it doesn't keep a reference to this activity. We can do this // by clearing its DatasetObservers, which setListAdapter(null) does. lv.setAdapter(mAdapter); mAdapter = null; getActivity().unregisterReceiver(mScanListener); super.onDestroy(); } @Override public void onResume() { super.onResume(); MusicUtils.setSpinnerState(getActivity()); MusicUtils.updateNowPlaying(getActivity()); } @Override public void onPause() { mReScanHandler.removeCallbacksAndMessages(null); super.onPause(); } private BroadcastReceiver mScanListener = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { MusicUtils.setSpinnerState(getActivity()); mReScanHandler.sendEmptyMessage(0); } }; private Handler mReScanHandler = new Handler() { @Override public void handleMessage(Message msg) { if (mAdapter != null) { getPlaylistCursor(mAdapter.getQueryHandler(), null); } } }; public void init(Cursor cursor) { if (mAdapter == null) { return; } mAdapter.changeCursor(cursor); if (mPlaylistCursor == null) { MusicUtils.displayDatabaseError(getActivity()); getActivity().closeContextMenu(); mReScanHandler.sendEmptyMessageDelayed(0, 1000); return; } // restore previous position if (mLastListPosCourse >= 0) { lv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine); mLastListPosCourse = -1; } MusicUtils.hideDatabaseError(getActivity()); } public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) { if (mCreateShortcut) { return; } AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn; menu.add(0, PLAY_SELECTION, 0, R.string.play_selection); if (mi.id >= 0 /*|| mi.id == PODCASTS_PLAYLIST*/) { menu.add(0, DELETE_PLAYLIST, 0, R.string.delete_playlist_menu); } if (mi.id == RECENTLY_ADDED_PLAYLIST) { menu.add(0, EDIT_PLAYLIST, 0, R.string.edit_playlist_menu); } if (mi.id >= 0) { menu.add(0, RENAME_PLAYLIST, 0, R.string.rename_playlist_menu); } mPlaylistCursor.moveToPosition(mi.position); menu.setHeaderTitle( mPlaylistCursor.getString(mPlaylistCursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME))); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case SCAN_DONE: if (resultCode == getActivity().RESULT_CANCELED) { getActivity().finish(); } else if (mAdapter != null) { getPlaylistCursor(mAdapter.getQueryHandler(), null); } break; } } private void playRecentlyAdded() { // do a query for all songs added in the last X weeks int X = MusicUtils.getIntPref(getActivity(), "numweeks", 2) * (3600 * 24 * 7); final String[] ccols = new String[] { MediaStore.Audio.Media._ID }; String where = MediaStore.MediaColumns.DATE_ADDED + ">" + (System.currentTimeMillis() / 1000 - X); Cursor cursor = MusicUtils.query(getActivity(), MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ccols, where, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); if (cursor == null) { // Todo: show a message return; } try { int len = cursor.getCount(); long[] list = new long[len]; for (int i = 0; i < len; i++) { cursor.moveToNext(); list[i] = cursor.getLong(0); } MusicUtils.playAll(getActivity(), list, 0); } catch (SQLiteException ex) { } finally { cursor.close(); } } private void playPodcasts() { // do a query for all files that are podcasts final String[] ccols = new String[] { MediaStore.Audio.Media._ID }; Cursor cursor = MusicUtils.query(getActivity(), MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ccols, MediaStore.Audio.Media.IS_PODCAST + "=1", null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); if (cursor == null) { // Todo: show a message return; } try { int len = cursor.getCount(); long[] list = new long[len]; for (int i = 0; i < len; i++) { cursor.moveToNext(); list[i] = cursor.getLong(0); } MusicUtils.playAll(getActivity(), list, 0); } catch (SQLiteException ex) { } finally { cursor.close(); } } String[] mCols = new String[] { MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME }; private Cursor getPlaylistCursor(AsyncQueryHandler async, String filterstring) { StringBuilder where = new StringBuilder(); where.append(MediaStore.Audio.Playlists.NAME + " != ''"); // Add in the filtering constraints String[] keywords = null; if (filterstring != null) { String[] searchWords = filterstring.split(" "); keywords = new String[searchWords.length]; Collator col = Collator.getInstance(); col.setStrength(Collator.PRIMARY); for (int i = 0; i < searchWords.length; i++) { keywords[i] = '%' + searchWords[i] + '%'; } for (int i = 0; i < searchWords.length; i++) { where.append(" AND "); where.append(MediaStore.Audio.Playlists.NAME + " LIKE ?"); } } String whereclause = where.toString(); if (async != null) { async.startQuery(0, null, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, mCols, whereclause, keywords, MediaStore.Audio.Playlists.NAME); return null; } Cursor c = null; c = MusicUtils.query(getActivity(), MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, mCols, whereclause, keywords, MediaStore.Audio.Playlists.NAME); return mergedCursor(c); } private Cursor mergedCursor(Cursor c) { if (c == null) { return null; } if (c instanceof MergeCursor) { // this shouldn't happen, but fail gracefully Log.d("PlaylistBrowserActivity", "Already wrapped"); return c; } MatrixCursor autoplaylistscursor = new MatrixCursor(mCols); if (mCreateShortcut) { ArrayList<Object> all = new ArrayList<Object>(2); all.add(ALL_SONGS_PLAYLIST); all.add(getString(R.string.play_all)); autoplaylistscursor.addRow(all); } ArrayList<Object> recent = new ArrayList<Object>(2); recent.add(RECENTLY_ADDED_PLAYLIST); recent.add(getString(R.string.recentlyadded)); autoplaylistscursor.addRow(recent); // check if there are any podcasts Cursor counter = MusicUtils.query(getActivity(), MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, new String[] { "count(*)" }, "is_podcast=1", null, null); if (counter != null) { counter.moveToFirst(); int numpodcasts = counter.getInt(0); counter.close(); if (numpodcasts > 0) { ArrayList<Object> podcasts = new ArrayList<Object>(2); podcasts.add(PODCASTS_PLAYLIST); podcasts.add(getString(R.string.podcasts_listitem)); autoplaylistscursor.addRow(podcasts); } } Cursor cc = new MergeCursor(new Cursor[] { autoplaylistscursor, c }); return cc; } static class PlaylistListAdapter extends SimpleCursorAdapter { int mTitleIdx; int mIdIdx; private PlaylistBrowserFragment mActivity = null; private AsyncQueryHandler mQueryHandler; private String mConstraint = null; private boolean mConstraintIsValid = false; class QueryHandler extends AsyncQueryHandler { QueryHandler(ContentResolver res) { super(res); } @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { //Log.i("@@@", "query complete: " + cursor.getCount() + " " + mActivity); if (cursor != null) { cursor = mActivity.mergedCursor(cursor); } mActivity.init(cursor); } } PlaylistListAdapter(Context context, PlaylistBrowserFragment currentactivity, int layout, Cursor cursor, String[] from, int[] to) { super(context, layout, cursor, from, to); mActivity = currentactivity; getColumnIndices(cursor); mQueryHandler = new QueryHandler(context.getContentResolver()); } private void getColumnIndices(Cursor cursor) { if (cursor != null) { mTitleIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME); mIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID); } } public void setActivity(PlaylistBrowserFragment newactivity) { mActivity = newactivity; } public AsyncQueryHandler getQueryHandler() { return mQueryHandler; } @Override public void bindView(View view, Context context, Cursor cursor) { TextView tv = (TextView) view.findViewById(R.id.line1); String name = cursor.getString(mTitleIdx); tv.setText(name); long id = cursor.getLong(mIdIdx); ImageView iv = (ImageView) view.findViewById(R.id.icon); if (id == RECENTLY_ADDED_PLAYLIST) { iv.setImageResource(R.drawable.ic_mp_playlist_recently_added_list); } else { iv.setImageResource(R.drawable.ic_mp_playlist_list); } ViewGroup.LayoutParams p = iv.getLayoutParams(); p.width = ViewGroup.LayoutParams.WRAP_CONTENT; p.height = ViewGroup.LayoutParams.WRAP_CONTENT; iv = (ImageView) view.findViewById(R.id.play_indicator); iv.setVisibility(View.GONE); view.findViewById(R.id.line2).setVisibility(View.GONE); } @Override public void changeCursor(Cursor cursor) { if (mActivity.getActivity().isFinishing() && cursor != null) { cursor.close(); cursor = null; } if (cursor != mActivity.mPlaylistCursor) { mActivity.mPlaylistCursor = cursor; super.changeCursor(cursor); getColumnIndices(cursor); } } @Override public Cursor runQueryOnBackgroundThread(CharSequence constraint) { String s = constraint.toString(); if (mConstraintIsValid && ((s == null && mConstraint == null) || (s != null && s.equals(mConstraint)))) { return getCursor(); } Cursor c = mActivity.getPlaylistCursor(null, s); mConstraint = s; mConstraintIsValid = true; return c; } } private Cursor mPlaylistCursor; }