목차
Progress Bar, Seek Bar
progress bar 를 움직였을 때 그 곳 부터 play 할려 한다면 palyer 의 seekTo() 를 호출해야 한다. (PlaybackService.get(this).seekToProgress() 안에서 호출한다.)그 부분에 대한 code 는 아래와 같다.
onProgressChanged()
--> send a message, MSG_SEEK_TO_PROGRESS
--> handleMessage
--> PlaybackService.get(this).seekToProgress()
--> updateElapsedTime()
// FullPlaybackActivity.java // implements SeekBar.OnSeekBarChangeListener public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.OnSeekBarChangeListener , View.OnLongClickListener private SeekBar mSeekBar; @Override public void onCreate(Bundle icicle) { mSeekBar = (SeekBar)findViewById(R.id.seek_bar); mSeekBar.setMax(1000); // set the max of the progress range mSeekBar.setOnSeekBarChangeListener(this); ... } ... /** * Update seek bar progress and schedule another update in one second */ private void updateElapsedTime() { long position = PlaybackService.hasInstance() ? PlaybackService.get(this).getPosition() : 0; if (!mSeekBarTracking) { long duration = mDuration; mSeekBar.setProgress(duration == 0 ? 0 : (int)(1000 * position / duration)); } mElapsedView.setText(DateUtils.formatElapsedTime(mTimeBuilder, position / 1000)); if (!mPaused && mControlsVisible && (mState & PlaybackService.FLAG_PLAYING) != 0) { // Try to update right after the duration increases by one second long next = 1050 - position % 1000; mUiHandler.removeMessages(MSG_UPDATE_PROGRESS); mUiHandler.sendEmptyMessageDelayed(MSG_UPDATE_PROGRESS, next); } } ... //--------------------------------------------------------- SeekBar.OnSeekBarChangeListener // ... @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { // set elapsed time mElapsedView.setText(DateUtils.formatElapsedTime(mTimeBuilder, progress * mDuration / 1000000)); // send a message for seeking mUiHandler.removeMessages(MSG_SEEK_TO_PROGRESS); mUiHandler.sendMessageDelayed(mUiHandler.obtainMessage(MSG_SEEK_TO_PROGRESS, progress, 0), 150); } } //------------------------------------------------------------------------ handleMessage /** * Calls {@link #seekToProgress()}. */ private static final int MSG_SEEK_TO_PROGRESS = 18; @Override public boolean handleMessage(Message message) { switch (message.what) { ... case MSG_SEEK_TO_PROGRESS: PlaybackService.get(this).seekToProgress(message.arg1); // practical action updateElapsedTime(); // update the progress bar and time break; default: return super.handleMessage(message); } return true; } }
// PlaybackService.java public final class PlaybackService extends Service implements Handler.Callback , MediaPlayer.OnCompletionListener , MediaPlayer.OnErrorListener , SharedPreferences.OnSharedPreferenceChangeListener , SongTimeline.Callback , SensorEventListener , AudioManager.OnAudioFocusChangeListener { ... /** * Seek to a position in the current song. * * @param progress Proportion of song completed (where 1000 is the end of the song) */ public void seekToProgress(int progress) { if (!mMediaPlayerInitialized) return; long position = (long)mMediaPlayer.getDuration() * progress / 1000; mMediaPlayer.seekTo((int)position); } ... }
Update Progress bar
노래등 음악파일이 재생될 때 progress bar 가 계속 해서 udpate 되도록 하는 부분이다.주기적으로 updateElapsedTime() 를 호출하는 것이 방법이다. 이를 위해서 MSG_UPDATE_PROGRESS 를 이용한다.
그리고 Service 와의 synch 를 위해 service 에서 값을 가져오고 있다. (PlaybackService.get(this).getPosition())
// FullPlaybackActivity.java public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.OnSeekBarChangeListener , View.OnLongClickListener private SeekBar mSeekBar; @Override public void onCreate(Bundle icicle) { ... setDuration(0); } @Override public void onResume() { super.onResume(); mPaused = false; updateElapsedTime(); } @Override public void onPause() { super.onPause(); mPaused = true; } ... /** * Update the current song duration fields. * * @param duration The new duration, in milliseconds. */ private void setDuration(long duration) { mDuration = duration; mDurationView.setText(DateUtils.formatElapsedTime(mTimeBuilder, duration / 1000)); } ... @Override protected void onStateChange(int state, int toggled) { super.onStateChange(state, toggled); ... if ((state & PlaybackService.FLAG_PLAYING) != 0) updateElapsedTime(); ... } @Override protected void onSongChange(Song song) { super.onSongChange(song); setDuration(song == null ? 0 : song.duration); ... mCurrentSong = song; updateElapsedTime(); if (mExtraInfoVisible) { mHandler.sendEmptyMessage(MSG_LOAD_EXTRA_INFO); } } ... /** * Update seek bar progress and schedule another update in one second */ private void updateElapsedTime() { long position = PlaybackService.hasInstance() ? PlaybackService.get(this).getPosition() : 0; if (!mSeekBarTracking) { long duration = mDuration; mSeekBar.setProgress(duration == 0 ? 0 : (int)(1000 * position / duration)); } mElapsedView.setText(DateUtils.formatElapsedTime(mTimeBuilder, position / 1000)); if (!mPaused && mControlsVisible && (mState & PlaybackService.FLAG_PLAYING) != 0) { // Try to update right after the duration increases by one second long next = 1050 - position % 1000; mUiHandler.removeMessages(MSG_UPDATE_PROGRESS); mUiHandler.sendEmptyMessageDelayed(MSG_UPDATE_PROGRESS, next); } } @Override public boolean handleMessage(Message message) { switch (message.what) { ... case MSG_UPDATE_PROGRESS: updateElapsedTime(); break; ... } } }
Rewind button
player button
button 을 누를 때 UI thread 에서 하는 일은 cover view 를 update 하는 정도이다. 실제 동작은 PlaybackService#shiftCurrentSong 을 통해 이루어 진다.
// FullPlaybackActivity.java public void onCreate(Bundle icicle){ ... View previousButton = findViewById(R.id.previous); previousButton.setOnClickListener(this); ... } // PlaybackActivity.java @Override public void onClick(View view) { switch (view.getId()) { case R.id.next: shiftCurrentSong(SongTimeline.SHIFT_NEXT_SONG); break; ... case R.id.previous: shiftCurrentSong(SongTimeline.SHIFT_PREVIOUS_SONG); break; ... } } @Override public void shiftCurrentSong(int delta) { setSong(PlaybackService.get(this).shiftCurrentSong(delta)); } protected void setSong(final Song song) { mLastSongEvent = SystemClock.uptimeMillis(); runOnUiThread(new Runnable() { @Override public void run() { onSongChange(song); } }); } /** * Called when the current song changes. * * @param song The new song */ protected void onSongChange(Song song) { // about Cover view if (mCoverView != null) mCoverView.querySongs(PlaybackService.get(this)); }
// PlaybackService.java /** * Move to next or previous song or album in the queue. * * @param delta One of SongTimeline.SHIFT_*. * @return The new current song. */ public Song shiftCurrentSong(int delta) { Song song = setCurrentSong(delta); userActionTriggered(); return song; } /** * Move to the next or previous song or album in the timeline. * * @param delta One of SongTimeline.SHIFT_*. 0 can also be passed to * initialize the current song with media player, notification, * broadcasts, etc. * @return The new current song */ private Song setCurrentSong(int delta) { if (mMediaPlayer == null) return null; if (mMediaPlayer.isPlaying()) mMediaPlayer.stop(); Song song; if (delta == 0) song = mTimeline.getSong(0); else song = mTimeline.shiftCurrentSong(delta); mCurrentSong = song; if (song == null || song.id == -1 || song.path == null) { if (MediaUtils.isSongAvailable(getContentResolver())) { int flag = finishAction(mState) == SongTimeline.FINISH_RANDOM ? FLAG_ERROR : FLAG_EMPTY_QUEUE; synchronized (mStateLock) { updateState((mState | flag) & ~FLAG_NO_MEDIA); } return null; } else { // we don't have any songs : / synchronized (mStateLock) { updateState((mState | FLAG_NO_MEDIA) & ~FLAG_EMPTY_QUEUE); } return null; } } else if ((mState & (FLAG_NO_MEDIA|FLAG_EMPTY_QUEUE)) != 0) { synchronized (mStateLock) { updateState(mState & ~(FLAG_EMPTY_QUEUE|FLAG_NO_MEDIA)); } } mHandler.removeMessages(PROCESS_SONG); mMediaPlayerInitialized = false; mHandler.sendMessage(mHandler.obtainMessage(PROCESS_SONG, song)); mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_CHANGE, -1, 0, song)); return song; } /** * Resets the idle timeout countdown. Should be called by a user action * has been triggered (new song chosen or playback toggled). * * If an idle fade out is actually in progress, aborts it and resets the * volume. */ public void userActionTriggered() { mHandler.removeMessages(FADE_OUT); mHandler.removeMessages(IDLE_TIMEOUT); ... long idleStart = mIdleStart; if (idleStart != -1 && SystemClock.elapsedRealtime() - idleStart < IDLE_GRACE_PERIOD) { mIdleStart = -1; setFlag(FLAG_PLAYING); } }
notification button
// PlaybackService.java public Notification createNotification(Song song, int state){ ... Intent previous = new Intent(PlaybackService.ACTION_PREVIOUS_SONG); previous.setComponent(service); expanded.setOnClickPendingIntent(R.id.previous, PendingIntent.getService(this, 0, previous, 0)); ... } public int onStartCommand(Intent intent, int flags, int startId){ ... else if (ACTION_PREVIOUS_SONG.equals(action)) { setCurrentSong(-1); userActionTriggered(); } else if (ACTION_REWIND_SONG.equals(action)) { /* only rewind song IF we played more than 2.5 sec (and song is longer than 5 sec) */ if(getPosition() > REWIND_AFTER_PLAYED_MS && getDuration() > REWIND_AFTER_PLAYED_MS*2) { setCurrentSong(0); } else { setCurrentSong(-1); } play(); }else if (ACTION_NEXT_SONG.equals(action)) { setCurrentSong(1); userActionTriggered(); } ... }
댓글 없음:
댓글 쓰기