Java tutorial
/* * ServeStream: A HTTP stream browser/player for Android * Copyright 2013 William Seemann * * 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 net.sourceforge.servestream.activity; import net.sourceforge.servestream.R; import net.sourceforge.servestream.button.RepeatingImageButton; import net.sourceforge.servestream.fragment.MediaPlayerFragment; import net.sourceforge.servestream.service.IMediaPlaybackService; import net.sourceforge.servestream.service.MediaPlaybackService; import net.sourceforge.servestream.utils.LoadingDialog; import net.sourceforge.servestream.utils.LoadingDialog.LoadingDialogListener; import net.sourceforge.servestream.utils.MusicUtils; import net.sourceforge.servestream.preference.PreferenceConstants; import net.sourceforge.servestream.preference.UserPreferences; import net.sourceforge.servestream.utils.MusicUtils.ServiceToken; import net.sourceforge.servestream.utils.SleepTimerDialog; import net.sourceforge.servestream.utils.SleepTimerDialog.SleepTimerDialogListener; import net.sourceforge.servestream.utils.Utils; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Configuration; import android.database.ContentObserver; import android.media.AudioManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.preference.PreferenceManager; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.MenuItemCompat; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import android.widget.SeekBar.OnSeekBarChangeListener; public class MediaPlayerActivity extends ActionBarActivity implements MusicUtils.Defs, OnSharedPreferenceChangeListener, LoadingDialogListener, SleepTimerDialogListener { private static final String TAG = MediaPlayerActivity.class.getName(); private static final String LOADING_DIALOG = "loading_dialog"; private static final String SLEEP_TIMER_DIALOG = "sleep_timer_dialog"; private DialogFragment mLoadingDialog; private int mParentActivityState = VISIBLE; private static int VISIBLE = 1; private static int GONE = 2; private SharedPreferences mPreferences; private boolean mSeeking = false; private boolean mDeviceHasDpad; private long mStartSeekPos = 0; private long mLastSeekEventTime; private IMediaPlaybackService mService = null; private RepeatingImageButton mPrevButton; private ImageButton mPauseButton; private RepeatingImageButton mNextButton; private ImageButton mRepeatButton; private ImageButton mShuffleButton; private Toast mToast; private ServiceToken mToken; private VolumeObserver mVolumeObserver; private ViewPager mPager; private GridPagerAdapter mAdapter; public MediaPlayerActivity() { } /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { setTheme(UserPreferences.getTheme()); super.onCreate(icicle); setContentView(R.layout.activity_media_player); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); //actionBar.setDisplayShowCustomEnabled(true); //actionBar.setDisplayShowTitleEnabled(false); LayoutInflater inflater = getLayoutInflater(); View v = inflater.inflate(R.layout.action_bar_media_player_header, null); //actionBar.setCustomView(v); setVolumeControlStream(AudioManager.STREAM_MUSIC); mPreferences = PreferenceManager.getDefaultSharedPreferences(this); mPreferences.registerOnSharedPreferenceChangeListener(this); mCurrentTime = (TextView) findViewById(R.id.position_text); mTotalTime = (TextView) findViewById(R.id.duration_text); mProgress = (ProgressBar) findViewById(R.id.seek_bar); mPrevButton = (RepeatingImageButton) findViewById(R.id.previous_button); mPrevButton.setOnClickListener(mPrevListener); mPrevButton.setRepeatListener(mRewListener, 260); mPauseButton = (ImageButton) findViewById(R.id.play_pause_button); mPauseButton.setOnClickListener(mPauseListener); mNextButton = (RepeatingImageButton) findViewById(R.id.next_button); mNextButton.setOnClickListener(mNextListener); mNextButton.setRepeatListener(mFfwdListener, 260); seekmethod = 1; mDeviceHasDpad = (getResources().getConfiguration().navigation == Configuration.NAVIGATION_DPAD); mShuffleButton = (ImageButton) findViewById(R.id.shuffle_button); mShuffleButton.setOnClickListener(mShuffleListener); mRepeatButton = (ImageButton) findViewById(R.id.repeat_button); mRepeatButton.setOnClickListener(mRepeatListener); if (mProgress instanceof SeekBar) { SeekBar seeker = (SeekBar) mProgress; seeker.setOnSeekBarChangeListener(mSeekListener); } mProgress.setMax(1000); mPager = (ViewPager) findViewById(R.id.pager); } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(PreferenceConstants.WAKELOCK)) { if (sharedPreferences.getBoolean(PreferenceConstants.WAKELOCK, true)) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } } private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { public void onStartTrackingTouch(SeekBar bar) { mLastSeekEventTime = 0; mFromTouch = true; } public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { if (!fromuser || (mService == null)) return; long now = SystemClock.elapsedRealtime(); if ((now - mLastSeekEventTime) > 250) { mLastSeekEventTime = now; mPosOverride = mDuration * progress / 1000; try { mService.seek(mPosOverride); } catch (RemoteException ex) { } // trackball event, allow progress updates if (!mFromTouch) { refreshNow(); mPosOverride = -1; } } } public void onStopTrackingTouch(SeekBar bar) { mPosOverride = -1; mFromTouch = false; } }; private OnSeekBarChangeListener mVolumeListener = new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }; private View.OnClickListener mShuffleListener = new View.OnClickListener() { public void onClick(View v) { toggleShuffle(); } }; private View.OnClickListener mRepeatListener = new View.OnClickListener() { public void onClick(View v) { cycleRepeat(); } }; private View.OnClickListener mPauseListener = new View.OnClickListener() { public void onClick(View v) { doPauseResume(); } }; private View.OnClickListener mPrevListener = new View.OnClickListener() { public void onClick(View v) { if (mService == null) return; try { mService.prev(); } catch (RemoteException ex) { } } }; private View.OnClickListener mNextListener = new View.OnClickListener() { public void onClick(View v) { if (mService == null) return; try { mService.next(); } catch (RemoteException ex) { } } }; private RepeatingImageButton.RepeatListener mRewListener = new RepeatingImageButton.RepeatListener() { public void onRepeat(View v, long howlong, int repcnt) { scanBackward(repcnt, howlong); } }; private RepeatingImageButton.RepeatListener mFfwdListener = new RepeatingImageButton.RepeatListener() { public void onRepeat(View v, long howlong, int repcnt) { scanForward(repcnt, howlong); } }; @Override public void onStart() { super.onStart(); paused = false; if (mPreferences.getBoolean(PreferenceConstants.WAKELOCK, true)) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } mToken = MusicUtils.bindToService(this, osc); if (mToken == null) { // something went wrong mHandler.sendEmptyMessage(QUIT); } mVolumeObserver = new VolumeObserver(new Handler()); getApplicationContext().getContentResolver() .registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver); IntentFilter f = new IntentFilter(); f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED); f.addAction(MediaPlaybackService.META_CHANGED); f.addAction(MediaPlaybackService.START_DIALOG); f.addAction(MediaPlaybackService.STOP_DIALOG); registerReceiver(mStatusListener, new IntentFilter(f)); updateTrackInfo(); long next = refreshNow(); queueNextRefresh(next); } @Override public void onResume() { super.onResume(); mParentActivityState = VISIBLE; updateTrackInfo(); setPauseButtonImage(); updateVolumeBar(); } @Override public void onPause() { super.onPause(); mParentActivityState = GONE; dismissLoadingDialog(); } @Override public void onStop() { paused = true; mHandler.removeMessages(REFRESH); getApplicationContext().getContentResolver().unregisterContentObserver(mVolumeObserver); unregisterReceiver(mStatusListener); MusicUtils.unbindFromService(mToken); mService = null; super.onStop(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); finish(); return true; case (R.id.menu_play_queue): startActivity(new Intent(MediaPlayerActivity.this, NowPlayingActivity.class)); return true; case (R.id.menu_item_stop): doStop(); return true; case (R.id.menu_item_sleep_timer): showDialog(SLEEP_TIMER_DIALOG); return true; case (R.id.menu_item_settings): startActivity(new Intent(MediaPlayerActivity.this, SettingsActivity.class)); return true; case EFFECTS_PANEL: { try { Intent i = new Intent("android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"); i.putExtra("android.media.extra.AUDIO_SESSION", mService.getAudioSessionId()); startActivityForResult(i, EFFECTS_PANEL); return true; } catch (RemoteException e) { e.printStackTrace(); } } } return super.onOptionsItemSelected(item); } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); updateVolumeBar(); if (Build.VERSION.SDK_INT >= 9) { // Don't offer the audio effects display when running on an OS // before API level 9 because it relies on the getAudioSessionId method, // which isn't available until after API 8 if (menu.findItem(EFFECTS_PANEL) == null) { Intent i = new Intent("android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"); if (getPackageManager().resolveActivity(i, 0) != null) { menu.add(0, EFFECTS_PANEL, 0, R.string.list_menu_effects).setIcon(R.drawable.ic_menu_eq); } } else { MenuItem item = menu.findItem(EFFECTS_PANEL); if (item != null) { if (MusicUtils.getCurrentAudioId() >= 0) { item.setVisible(true); } else { item.setVisible(false); } } } } return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); // Inflate the menu; this adds items to the action bar if it is present. inflater.inflate(R.menu.media_player, menu); final MenuItem item = menu.findItem(R.id.menu_volume); View view = MenuItemCompat.getActionView(item); mVolume = (SeekBar) view.findViewById(R.id.volume_bar); mVolume.setOnSeekBarChangeListener(mVolumeListener); return true; } private final int keyboard[][] = { { KeyEvent.KEYCODE_Q, KeyEvent.KEYCODE_W, KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_T, KeyEvent.KEYCODE_Y, KeyEvent.KEYCODE_U, KeyEvent.KEYCODE_I, KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_P, }, { KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_F, KeyEvent.KEYCODE_G, KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_J, KeyEvent.KEYCODE_K, KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_DEL, }, { KeyEvent.KEYCODE_Z, KeyEvent.KEYCODE_X, KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_V, KeyEvent.KEYCODE_B, KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_M, KeyEvent.KEYCODE_COMMA, KeyEvent.KEYCODE_PERIOD, KeyEvent.KEYCODE_ENTER } }; private int lastX; private int lastY; private boolean seekMethod1(int keyCode) { if (mService == null) return false; for (int x = 0; x < 10; x++) { for (int y = 0; y < 3; y++) { if (keyboard[y][x] == keyCode) { int dir = 0; // top row if (x == lastX && y == lastY) dir = 0; else if (y == 0 && lastY == 0 && x > lastX) dir = 1; else if (y == 0 && lastY == 0 && x < lastX) dir = -1; // bottom row else if (y == 2 && lastY == 2 && x > lastX) dir = -1; else if (y == 2 && lastY == 2 && x < lastX) dir = 1; // moving up else if (y < lastY && x <= 4) dir = 1; else if (y < lastY && x >= 5) dir = -1; // moving down else if (y > lastY && x <= 4) dir = -1; else if (y > lastY && x >= 5) dir = 1; lastX = x; lastY = y; try { mService.seek(mService.position() + dir * 5); } catch (RemoteException ex) { } refreshNow(); return true; } } } lastX = -1; lastY = -1; return false; } private boolean seekMethod2(int keyCode) { if (mService == null) return false; for (int i = 0; i < 10; i++) { if (keyboard[0][i] == keyCode) { int seekpercentage = 100 * i / 10; try { mService.seek(mService.duration() * seekpercentage / 100); } catch (RemoteException ex) { } refreshNow(); return true; } } return false; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { try { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: if (!useDpadMusicControl()) { break; } if (mService != null) { if (!mSeeking && mStartSeekPos >= 0) { mPauseButton.requestFocus(); if (mStartSeekPos < 1000) { mService.prev(); } else { mService.seek(0); } } else { scanBackward(-1, event.getEventTime() - event.getDownTime()); mPauseButton.requestFocus(); mStartSeekPos = -1; } } mSeeking = false; mPosOverride = -1; return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!useDpadMusicControl()) { break; } if (mService != null) { if (!mSeeking && mStartSeekPos >= 0) { mPauseButton.requestFocus(); mService.next(); } else { scanForward(-1, event.getEventTime() - event.getDownTime()); mPauseButton.requestFocus(); mStartSeekPos = -1; } } mSeeking = false; mPosOverride = -1; return true; } } catch (RemoteException ex) { } return super.onKeyUp(keyCode, event); } private boolean useDpadMusicControl() { if (mDeviceHasDpad && (mPrevButton.isFocused() || mNextButton.isFocused() || mPauseButton.isFocused())) { return true; } return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { int repcnt = event.getRepeatCount(); if ((seekmethod == 0) ? seekMethod1(keyCode) : seekMethod2(keyCode)) return true; switch (keyCode) { case KeyEvent.KEYCODE_SLASH: seekmethod = 1 - seekmethod; return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!useDpadMusicControl()) { break; } if (!mPrevButton.hasFocus()) { mPrevButton.requestFocus(); } scanBackward(repcnt, event.getEventTime() - event.getDownTime()); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!useDpadMusicControl()) { break; } if (!mNextButton.hasFocus()) { mNextButton.requestFocus(); } scanForward(repcnt, event.getEventTime() - event.getDownTime()); return true; case KeyEvent.KEYCODE_S: toggleShuffle(); return true; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_SPACE: doPauseResume(); return true; } return super.onKeyDown(keyCode, event); } private void scanBackward(int repcnt, long delta) { if (mService == null) return; try { if (repcnt == 0) { mStartSeekPos = mService.position(); mLastSeekEventTime = 0; mSeeking = false; } else { mSeeking = true; if (delta < 5000) { // seek at 10x speed for the first 5 seconds delta = delta * 10; } else { // seek at 40x after that delta = 50000 + (delta - 5000) * 40; } long newpos = mStartSeekPos - delta; if (newpos < 0) { // move to previous track mService.prev(); long duration = mService.duration(); mStartSeekPos += duration; newpos += duration; } if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) { mService.seek(newpos); mLastSeekEventTime = delta; } if (repcnt >= 0) { mPosOverride = newpos; } else { mPosOverride = -1; } refreshNow(); } } catch (RemoteException ex) { } } private void scanForward(int repcnt, long delta) { if (mService == null) return; try { if (repcnt == 0) { mStartSeekPos = mService.position(); mLastSeekEventTime = 0; mSeeking = false; } else { mSeeking = true; if (delta < 5000) { // seek at 10x speed for the first 5 seconds delta = delta * 10; } else { // seek at 40x after that delta = 50000 + (delta - 5000) * 40; } long newpos = mStartSeekPos + delta; long duration = mService.duration(); if (newpos >= duration) { // move to next track mService.next(); mStartSeekPos -= duration; // is OK to go negative newpos -= duration; } if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) { mService.seek(newpos); mLastSeekEventTime = delta; } if (repcnt >= 0) { mPosOverride = newpos; } else { mPosOverride = -1; } refreshNow(); } } catch (RemoteException ex) { } } private void doPauseResume() { try { if (mService != null) { if (mService.isPlaying()) { mService.pause(); } else { mService.play(); } refreshNow(); setPauseButtonImage(); } } catch (RemoteException ex) { } } private void doStop() { try { if (mService != null) { mService.stop(); refreshNow(); setPauseButtonImage(); } } catch (RemoteException ex) { } } private void toggleShuffle() { if (mService == null) { return; } try { int shuffle = mService.getShuffleMode(); if (shuffle == MediaPlaybackService.SHUFFLE_NONE) { mService.setShuffleMode(MediaPlaybackService.SHUFFLE_ON); if (mService.getRepeatMode() == MediaPlaybackService.REPEAT_CURRENT) { mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL); setRepeatButtonImage(); } showToast(R.string.shuffle_on_notif); } else if (shuffle == MediaPlaybackService.SHUFFLE_ON) { mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE); showToast(R.string.shuffle_off_notif); } else { Log.e(TAG, "Invalid shuffle mode: " + shuffle); } setShuffleButtonImage(); } catch (RemoteException ex) { } } private void cycleRepeat() { if (mService == null) { return; } try { int mode = mService.getRepeatMode(); if (mode == MediaPlaybackService.REPEAT_NONE) { mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL); showToast(R.string.repeat_all_notif); } else if (mode == MediaPlaybackService.REPEAT_ALL) { mService.setRepeatMode(MediaPlaybackService.REPEAT_CURRENT); if (mService.getShuffleMode() != MediaPlaybackService.SHUFFLE_NONE) { mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE); setShuffleButtonImage(); } showToast(R.string.repeat_current_notif); } else { mService.setRepeatMode(MediaPlaybackService.REPEAT_NONE); showToast(R.string.repeat_off_notif); } setRepeatButtonImage(); } catch (RemoteException ex) { } } private void setSleepTimer(int pos) { if (mService == null) { return; } try { MusicUtils.sService.setSleepTimerMode(pos); if (pos == MediaPlaybackService.SLEEP_TIMER_OFF) { showToast(R.string.sleep_timer_off_notif); } else { showToast(getString(R.string.sleep_timer_on_notif) + " " + makeTimeString(pos)); } } catch (RemoteException e) { } } private String makeTimeString(int pos) { String minuteText; if (pos == MediaPlaybackService.SLEEP_TIMER_OFF) { minuteText = getResources().getString(R.string.disable_sleeptimer_label); } else if (pos == 1) { minuteText = getResources().getString(R.string.minute); } else if (pos % 60 == 0 && pos > 60) { minuteText = getResources().getString(R.string.hours, String.valueOf(pos / 60)); } else if (pos % 60 == 0) { minuteText = getResources().getString(R.string.hour); } else { minuteText = getResources().getString(R.string.minutes, String.valueOf(pos)); } return minuteText; } private void showToast(int resid) { if (mToast == null) { mToast = Toast.makeText(this, null, Toast.LENGTH_SHORT); } mToast.setText(resid); mToast.show(); } private void showToast(String message) { if (mToast == null) { mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT); } mToast.setText(message); mToast.show(); } private void startPlayback() { if (mService == null) return; updateTrackInfo(); long next = refreshNow(); queueNextRefresh(next); } private ServiceConnection osc = new ServiceConnection() { public void onServiceConnected(ComponentName classname, IBinder obj) { mService = IMediaPlaybackService.Stub.asInterface(obj); startPlayback(); try { // Assume something is playing when the service says it is, // but also if the audio ID is valid but the service is paused. if (mService.getAudioId() >= 0 || mService.isPlaying() || mService.getPath() != null) { // something is playing now, we're done mRepeatButton.setVisibility(View.VISIBLE); mShuffleButton.setVisibility(View.VISIBLE); setRepeatButtonImage(); setShuffleButtonImage(); setPauseButtonImage(); initPager(); return; } } catch (RemoteException ex) { } // Service is dead or not playing anything. Return to the previous // activity. finish(); } public void onServiceDisconnected(ComponentName classname) { mService = null; } }; private void setRepeatButtonImage() { if (mService == null) return; try { switch (mService.getRepeatMode()) { case MediaPlaybackService.REPEAT_ALL: mRepeatButton.setImageResource(R.drawable.ic_av_repeat_selected); break; case MediaPlaybackService.REPEAT_CURRENT: mRepeatButton.setImageResource(R.drawable.ic_av_repeat_one_selected); break; default: mRepeatButton.setImageResource(Utils.getThemedIcon(this, R.attr.ic_av_repeat)); break; } } catch (RemoteException ex) { } } private void setShuffleButtonImage() { if (mService == null) return; try { switch (mService.getShuffleMode()) { case MediaPlaybackService.SHUFFLE_NONE: mShuffleButton.setImageResource(Utils.getThemedIcon(this, R.attr.ic_av_shuffle)); break; default: mShuffleButton.setImageResource(R.drawable.ic_av_shuffle_selected); break; } } catch (RemoteException ex) { } } private void setPauseButtonImage() { try { if (mService != null && mService.isPlaying()) { mPauseButton.setImageResource(Utils.getThemedIcon(this, R.attr.ic_av_pause_over_video_large)); } else { mPauseButton.setImageResource(Utils.getThemedIcon(this, R.attr.ic_av_play_over_video_large)); } } catch (RemoteException ex) { } } private void setSeekControls() { if (mService == null) { return; } try { if (mService.duration() > 0) { mProgress.setEnabled(true); mPrevButton.setRepeatListener(mRewListener, 260); mNextButton.setRepeatListener(mFfwdListener, 260); } else { mProgress.setEnabled(false); mPrevButton.setRepeatListener(null, -1); mNextButton.setRepeatListener(null, -1); } } catch (RemoteException e) { } } private void initPager() { if (mService == null) { return; } int count = 0; try { count = mService.getQueue().length + 2; } catch (RemoteException e) { finish(); } mAdapter = new GridPagerAdapter(getSupportFragmentManager(), count); mPager.setAdapter(mAdapter); mPager.setOnPageChangeListener(new OnPageChangeListener() { boolean mFromUser = false; @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_DRAGGING) { mFromUser = true; } else if (state == ViewPager.SCROLL_STATE_IDLE) { mFromUser = false; } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { try { if (mFromUser) { position--; if (position == -1) { position = mService.getQueue().length - 1; } else if (position == mService.getQueue().length) { position = 0; } mService.setQueuePosition(position); mFromUser = false; } } catch (RemoteException e) { e.printStackTrace(); } } }); setPager(); } private void setPager() { if (mService == null) { return; } try { int position = mService.getQueuePosition() + 1; mPager.setCurrentItem(position, false); } catch (RemoteException e) { e.printStackTrace(); } } private TextView mCurrentTime; private TextView mTotalTime; private ProgressBar mProgress; private SeekBar mVolume; private long mPosOverride = -1; private boolean mFromTouch = false; private long mDuration; private int seekmethod; private boolean paused; private static final int REFRESH = 1; private static final int QUIT = 2; private void queueNextRefresh(long delay) { if (!paused) { Message msg = mHandler.obtainMessage(REFRESH); mHandler.removeMessages(REFRESH); mHandler.sendMessageDelayed(msg, delay); } } private long refreshNow() { if (mService == null) return 500; try { long pos = mPosOverride < 0 ? mService.position() : mPosOverride; if ((pos >= 0)) { mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000)); if (mDuration > 0) { mProgress.setProgress((int) (1000 * pos / mDuration)); } else { mProgress.setProgress(1000); } if (mService.isPlaying()) { mCurrentTime.setVisibility(View.VISIBLE); } else { // blink the counter int vis = mCurrentTime.getVisibility(); mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE); return 500; } } else { mCurrentTime.setText("--:--"); mProgress.setProgress(1000); } // calculate the number of milliseconds until the next full second, so // the counter can be updated at just the right time long remaining = 1000 - (pos % 1000); // approximate how often we would need to refresh the slider to // move it smoothly int width = mProgress.getWidth(); if (width == 0) width = 320; long smoothrefreshtime = mDuration / width; if (smoothrefreshtime > remaining) return remaining; if (smoothrefreshtime < 20) return 20; return smoothrefreshtime; } catch (RemoteException ex) { } return 500; } private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case REFRESH: long next = refreshNow(); queueNextRefresh(next); break; case QUIT: // This can be moved back to onCreate once the bug that prevents // Dialogs from being started from onCreate/onResume is fixed. new AlertDialog.Builder(MediaPlayerActivity.this).setTitle(R.string.service_start_error_title) .setMessage(R.string.service_start_error_msg) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { finish(); } }).setCancelable(false).show(); break; default: break; } } }; private BroadcastReceiver mStatusListener = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(MediaPlaybackService.META_CHANGED)) { // redraw the artist/title info and // set new max for progress bar updateTrackInfo(); setSeekControls(); setPauseButtonImage(); queueNextRefresh(1); setPager(); } else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) { setPauseButtonImage(); } else if (action.equals(MediaPlaybackService.START_DIALOG)) { if (mParentActivityState == VISIBLE) { showLoadingDialog(); } } else if (action.equals(MediaPlaybackService.STOP_DIALOG)) { if (mParentActivityState == VISIBLE) { dismissLoadingDialog(); } } } }; private void updateTrackInfo() { if (mService == null) { return; } try { mDuration = mService.duration(); mTotalTime.setText(MusicUtils.makeTimeString(this, mDuration / 1000)); } catch (RemoteException ex) { finish(); } } public synchronized void showLoadingDialog() { mLoadingDialog = LoadingDialog.newInstance(this, getString(R.string.opening_url_message)); mLoadingDialog.show(getSupportFragmentManager(), LOADING_DIALOG); } public synchronized void dismissLoadingDialog() { if (mLoadingDialog != null) { mLoadingDialog.dismiss(); mLoadingDialog = null; } } private synchronized void updateVolumeBar() { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int curVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); if (mVolume != null) { mVolume.setProgress(curVolume); mVolume.setMax(maxVolume); } } public void showDialog(String tag) { // DialogFragment.show() will take care of adding the fragment // in a transaction. We also want to remove any currently showing // dialog, so make our own transaction and take care of that here. FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Fragment prev = getSupportFragmentManager().findFragmentByTag(tag); if (prev != null) { ft.remove(prev); } DialogFragment newFragment = null; // Create and show the dialog. if (tag.equals(SLEEP_TIMER_DIALOG)) { if (mService == null) { return; } try { newFragment = SleepTimerDialog.newInstance(this, mService.getSleepTimerMode()); } catch (RemoteException e) { } } ft.add(0, newFragment, tag); ft.commit(); } public void dismissDialog(String tag) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); DialogFragment prev = (DialogFragment) getSupportFragmentManager().findFragmentByTag(tag); if (prev != null) { prev.dismiss(); ft.remove(prev); } ft.commit(); } @Override public void onLoadingDialogCancelled(DialogFragment dialog) { } @Override public void onSleepTimerSet(DialogFragment dialog, int pos) { setSleepTimer(pos); } private class VolumeObserver extends ContentObserver { public VolumeObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { onChange(selfChange, null); } @Override public void onChange(boolean selfChange, Uri uri) { updateVolumeBar(); } } private class GridPagerAdapter extends FragmentStatePagerAdapter { private int mCount; public GridPagerAdapter(FragmentManager fm, int count) { super(fm); mCount = count; } @Override public Fragment getItem(int position) { return new MediaPlayerFragment(); } @Override public int getCount() { return mCount; } @Override public Parcelable saveState() { return null; } } }