mediaplaybackservice.java
来自「Android平台上的media player, iPhone风格」· Java 代码 · 共 1,619 行 · 第 1/4 页
JAVA
1,619 行
where = null; selectionArgs = null; } else { uri = MediaStore.Audio.Media.getContentUriForPath(path); where = MediaStore.Audio.Media.DATA + "=?"; selectionArgs = new String[] { path }; } try { mCursor = resolver.query(uri, mCursorCols, where, selectionArgs, null); if (mCursor != null) { if (mCursor.getCount() == 0) { mCursor.close(); mCursor = null; } else { mCursor.moveToNext(); ensurePlayListCapacity(1); mPlayListLen = 1; mPlayList[0] = mCursor.getInt(0); mPlayPos = 0; } } } catch (UnsupportedOperationException ex) { } } mFileToPlay = path; mPlayer.setDataSource(mFileToPlay); mOneShot = oneshot; if (! mPlayer.isInitialized()) { stop(true); if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) { // beware: this ends up being recursive because next() calls open() again. next(false); } if (! mPlayer.isInitialized() && mOpenFailedCounter != 0) { // need to make sure we only shows this once mOpenFailedCounter = 0; Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show(); } } else { mOpenFailedCounter = 0; } } } /** * Starts playback of a previously opened file. */ public void play() { if (mPlayer.isInitialized()) { mPlayer.start(); setForeground(true); mWasPlaying = true; NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar); views.setImageViewResource(R.id.icon, R.drawable.stat_notify_musicplayer); views.setTextViewText(R.id.trackname, getTrackName()); String artist = getArtistName(); if (artist == null || artist.equals(MediaFile.UNKNOWN_STRING)) { artist = getString(R.string.unknown_artist_name); } String album = getAlbumName(); if (album == null || album.equals(MediaFile.UNKNOWN_STRING)) { album = getString(R.string.unknown_album_name); } views.setTextViewText(R.id.artistalbum, getString(R.string.notification_artist_album, artist, album) ); Intent statusintent = new Intent("com.android.imusic.playing"); statusintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Notification status = new Notification(); status.contentView = views; status.flags |= Notification.FLAG_ONGOING_EVENT; status.icon = R.drawable.stat_notify_musicplayer; status.contentIntent = PendingIntent.getActivity(this, 0, new Intent("com.android.imusic.playing"), 0); nm.notify(PLAYBACKSERVICE_STATUS, status); notifyChange(PLAYSTATE_CHANGED); } } private void stop(boolean remove_status_icon) { if (mPlayer.isInitialized()) { mPlayer.stop(); } mFileToPlay = null; if (mCursor != null) { mCursor.close(); mCursor = null; } if (remove_status_icon) { gotoIdleState(); } setForeground(false); mWasPlaying = false; } /** * Stops playback. */ public void stop() { stop(true); } /** * Pauses playback (call play() to resume) */ public void pause() { if (isPlaying()) { mPlayer.pause(); gotoIdleState(); setForeground(false); mWasPlaying = false; notifyChange(PLAYSTATE_CHANGED); } } /** Returns whether playback is currently paused * * @return true if playback is paused, false if not */ public boolean isPlaying() { if (mPlayer.isInitialized()) { return mPlayer.isPlaying(); } return false; } /* Desired behavior for prev/next/shuffle: - NEXT will move to the next track in the list when not shuffling, and to a track randomly picked from the not-yet-played tracks when shuffling. If all tracks have already been played, pick from the full set, but avoid picking the previously played track if possible. - when shuffling, PREV will go to the previously played track. Hitting PREV again will go to the track played before that, etc. When the start of the history has been reached, PREV is a no-op. When not shuffling, PREV will go to the sequentially previous track (the difference with the shuffle-case is mainly that when not shuffling, the user can back up to tracks that are not in the history). Example: When playing an album with 10 tracks from the start, and enabling shuffle while playing track 5, the remaining tracks (6-10) will be shuffled, e.g. the final play order might be 1-2-3-4-5-8-10-6-9-7. When hitting 'prev' 8 times while playing track 7 in this example, the user will go to tracks 9-6-10-8-5-4-3-2. If the user then hits 'next', a random track will be picked again. If at any time user disables shuffling the next/previous track will be picked in sequential order again. */ public void prev() { synchronized (this) { if (mOneShot) { // we were playing a specific file not part of a playlist, so there is no 'previous' seek(0); play(); return; } if (mShuffleMode == SHUFFLE_NORMAL) { // go to previously-played track and remove it from the history int histsize = mHistory.size(); if (histsize == 0) { // prev is a no-op return; } Integer pos = mHistory.remove(histsize - 1); mPlayPos = pos.intValue(); } else { if (mPlayPos > 0) { mPlayPos--; } else { mPlayPos = mPlayListLen - 1; } } stop(false); openCurrent(); play(); notifyChange(META_CHANGED); } } public void next(boolean force) { synchronized (this) { if (mOneShot) { // we were playing a specific file not part of a playlist, so there is no 'next' seek(0); play(); return; } // Store the current file in the history, but keep the history at a // reasonable size mHistory.add(Integer.valueOf(mPlayPos)); if (mHistory.size() > MAX_HISTORY_SIZE) { mHistory.removeElementAt(0); } if (mShuffleMode == SHUFFLE_NORMAL) { // Pick random next track from the not-yet-played ones // TODO: make it work right after adding/removing items in the queue. int numTracks = mPlayListLen; int[] tracks = new int[numTracks]; for (int i=0;i < numTracks; i++) { tracks[i] = i; } int numHistory = mHistory.size(); int numUnplayed = numTracks; for (int i=0;i < numHistory; i++) { int idx = mHistory.get(i).intValue(); if (idx < numTracks && tracks[idx] >= 0) { numUnplayed--; tracks[idx] = -1; } } // 'numUnplayed' now indicates how many tracks have not yet // been played, and 'tracks' contains the indices of those // tracks. if (numUnplayed <=0) { // everything's already been played if (mRepeatMode == REPEAT_ALL || force) { //pick from full set numUnplayed = numTracks; for (int i=0;i < numTracks; i++) { tracks[i] = i; } } else { // all done gotoIdleState(); return; } } int skip = mRand.nextInt(numUnplayed); int cnt = -1; while (true) { while (tracks[++cnt] < 0) ; skip--; if (skip < 0) { break; } } mPlayPos = cnt; } else if (mShuffleMode == SHUFFLE_AUTO) { doAutoShuffleUpdate(); mPlayPos++; } else { if (mPlayPos >= mPlayListLen - 1) { // we're at the end of the list if (mRepeatMode == REPEAT_NONE && !force) { // all done gotoIdleState(); notifyChange(PLAYBACK_COMPLETE); return; } else if (mRepeatMode == REPEAT_ALL || force) { mPlayPos = 0; } } else { mPlayPos++; } } stop(false); openCurrent(); play(); notifyChange(META_CHANGED); } } private void gotoIdleState() { NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); nm.cancel(PLAYBACKSERVICE_STATUS); mDelayedStopHandler.removeCallbacksAndMessages(null); Message msg = mDelayedStopHandler.obtainMessage(); mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY); } // Make sure there are at least 5 items after the currently playing item // and no more than 10 items before. private void doAutoShuffleUpdate() { // remove old entries if (mPlayPos > 10) { removeTracks(0, mPlayPos - 9); } // add new entries if needed int to_add = 7 - (mPlayListLen - (mPlayPos < 0 ? -1 : mPlayPos)); if (to_add > 0) { for (int i = 0; i < to_add; i++) { // pick something at random from the list int idx = mRand.nextInt(mAutoShuffleList.length); Integer which = mAutoShuffleList[idx]; addToPlayList(which); } notifyChange(QUEUE_CHANGED); } } // A simple variation of Random that makes sure that the // value it returns is not equal to the value it returned // previously, unless the interval is 1. private class Shuffler { private int mPrevious; private Random mRandom = new Random(); public int nextInt(int interval) { int ret; do { ret = mRandom.nextInt(interval); } while (ret == mPrevious && interval > 1); mPrevious = ret; return ret; } }; private boolean makeAutoShuffleList() { ContentResolver res = getContentResolver(); Cursor c = null; try { c = res.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, new String[] {MediaStore.Audio.Media._ID}, MediaStore.Audio.Media.IS_MUSIC + "=1", null, null); if (c == null || c.getCount() == 0) { return false; } int len = c.getCount(); int[] list = new int[len]; for (int i = 0; i < len; i++) { c.moveToNext(); list[i] = c.getInt(0); } mAutoShuffleList = list; return true; } catch (RuntimeException ex) { } finally { if (c != null) { c.close(); } } return false; } /** * Removes the range of tracks specified from the play list. If a file within the range is * the file currently being played, playback will move to the next file after the * range. * @param first The first file to be removed * @param last The last file to be removed * @return the number of tracks deleted */ public int removeTracks(int first, int last) { synchronized (this) { if (last < first) return 0; if (first < 0) first = 0; if (last >= mPlayListLen) last = mPlayListLen - 1; boolean gotonext = false; if (first <= mPlayPos && mPlayPos <= last) { mPlayPos = first; gotonext = true; } else if (mPlayPos > last) { mPlayPos -= (last - first + 1); } int num = mPlayListLen - last - 1; for (int i = 0; i < num; i++) { mPlayList[first + i] = mPlayList[last + 1 + i]; } mPlayListLen -= last - first + 1; if (gotonext) { if (mPlayListLen == 0) { stop(true); mPlayPos = -1; } else { if (mPlayPos >= mPlayListLen) { mPlayPos = 0; } stop(false); openCurrent(); play(); } } notifyChange(QUEUE_CHANGED); return last - first + 1; } } /** * Removes all instances of the track with the given id * from the playlist. * @param id The id to be removed * @return how many instances of the track were removed */ public int removeTrack(int id) { int numremoved = 0; synchronized (this) { for (int i = 0; i < mPlayListLen; i++) { if (mPlayList[i] == id) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?