/** * */ package gilday.android.powerhour.view; import gilday.android.powerhour.IMusicUpdateListener; import gilday.android.powerhour.IProgressUpdateListener; import gilday.android.powerhour.MusicUpdateBroadcastReceiver; import gilday.android.powerhour.MusicUtils; import gilday.android.powerhour.PowerHourPreferences; import gilday.android.powerhour.ProgressUpdateBroadcastReceiver; import gilday.android.powerhour.R; import gilday.android.powerhour.data.PreferenceRepository; import gilday.android.powerhour.model.PlaylistItem; import gilday.android.powerhour.service.IPowerHourService; import gilday.android.powerhour.service.PowerHourService; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.AlertDialog; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.graphics.Typeface; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** * @author John Gilday * */ public class NowPlayingActivity extends Activity implements IMusicUpdateListener, IProgressUpdateListener { // http://developer.android.com/guide/practices/design/performance.html#avoid_enums private static final String TAG = "NowPlaying"; private IPowerHourService phService; private TextView artistText, albumText, songText, minutesText; private ImageButton pauseButton; private ProgressBar pBar; //private RelativeLayout layout; private ImageView artView; private MusicUpdateBroadcastReceiver musicUpdateReceiver; private ProgressUpdateBroadcastReceiver progressUpdateReceiver; private Timer progressUpdateTimer; private final int progressTimerInterval = 500; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setTitle(R.string.nowplaying_title); setContentView(R.layout.nowplaying); // Volume buttons will control media stream setVolumeControlStream(AudioManager.STREAM_MUSIC); artistText = (TextView)findViewById(R.id.ArtistTitle); albumText = (TextView)findViewById(R.id.AlbumTitle); songText = (TextView)findViewById(R.id.SongTitle); artView = (ImageView)findViewById(R.id.ArtView); minutesText = (TextView)findViewById(R.id.MinutesText); pauseButton = (ImageButton)findViewById(R.id.PauseButton); minutesText.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/COLLEGE.TTF")); pBar = (ProgressBar)findViewById(R.id.SongProgress); pBar.setMax(10000); } @Override protected void onResume(){ super.onResume(); // See if the service is running if(isPowerHourServiceRunning()) { // Bind to the started service. Intent bindServiceIntent = new Intent(this, PowerHourService.class); // Bind WITHOUT auto create set if(!bindService(bindServiceIntent, phServiceConnection, 0)){ Log.e(TAG, "Could not bind to service"); } } else { // start activity to select the playlist Intent getList = new Intent(getBaseContext(), TitleScreen.class); getList.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivityForResult(getList, 0); } // Register receivers musicUpdateReceiver = new MusicUpdateBroadcastReceiver(this); progressUpdateReceiver = new ProgressUpdateBroadcastReceiver(this); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext()); lbm.registerReceiver(musicUpdateReceiver, new IntentFilter(PowerHourService.MUSIC_UPDATE_BROADCAST)); lbm.registerReceiver(progressUpdateReceiver, new IntentFilter(PowerHourService.PROGRESS_UPDATE_BROADCAST)); lbm.registerReceiver(progressUpdateReceiver, new IntentFilter(PowerHourService.PROGRESS_PAUSE_RESUME_BROADCAST)); } @Override protected void onPause(){ super.onPause(); if(phService != null) { // Unbind from the service b/c this activity is not visible unbindService(phServiceConnection); phService = null; } // Unbind receivers musicUpdateReceiver.unRegisterUpdateListener(); progressUpdateReceiver.unregisterUpdateListener(); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext()); lbm.unregisterReceiver(musicUpdateReceiver); lbm.unregisterReceiver(progressUpdateReceiver); musicUpdateReceiver = null; progressUpdateReceiver = null; // Kill the local ProgressDisplayTimerTask if(progressUpdateTimer != null) { progressUpdateTimer.cancel(); progressUpdateTimer.purge(); progressUpdateTimer = null; } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if(resultCode == RESULT_CANCELED){ // Log.v(TAG, "Result is a cancel! Finish now"); finish(); } else{ // Log.v(TAG, "Going to get the result and bind to service"); Intent launchServiceIntent = new Intent(this, PowerHourService.class); // Send new intent to service startService(launchServiceIntent); } } public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.nowplaying_menu, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch(item.getItemId()){ case R.id.menu_stop: new AlertDialog.Builder(this) .setTitle(getString(R.string.quit_confirmation)) .setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Unbind if(phService != null) { unbindService(phServiceConnection); phService = null; } // Stop service stopService(new Intent(getApplicationContext(), PowerHourService.class)); // Kill this activity finish(); } }) .setNegativeButton(getString(android.R.string.cancel), null) .show(); break; case R.id.menu_skip: skipClick(null); break; case R.id.menu_preferences: Intent launchPreferencesIntent = new Intent().setClass(this, PowerHourPreferences.class); startActivity(launchPreferencesIntent); break; } return true; } private boolean isPowerHourServiceRunning() { ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for(RunningServiceInfo serviceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) { if(serviceInfo.service.getClassName().equals(PowerHourService.class.getName())) { return true; } } return false; } private ServiceConnection phServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { phService = (IPowerHourService)service; int playing = phService.getPlayingState(); int secondsElapsed = phService.getProgress(); int milisecondsElapsed = secondsElapsed * 1000; int nowplaying = phService.getCurrentSong(); // Q: Why do we have to check if these values are valid? // A: Due to a race condition, the Service could be "playing" // but it could still be loading the first song. This will // check if the player is attempting to play but not actually // playing if(secondsElapsed > 0 && nowplaying > 0){ //Log.d(TAG, "WARNING: minutesElapsed= " + minutesElapsed + " nowplaying= " + nowplaying); // First call safeUpdateUI with both arguments to update progress bar updateNowPlayingSongUI(nowplaying); updateMinutesText(secondsElapsed / 60); updateProgressBar(milisecondsElapsed); } // Update the pause button if the service is paused. // This happens when the phone receives a call and the service pauses itself if(playing == PowerHourService.PAUSED) { pauseButton.setImageResource(R.drawable.play); } else { pauseButton.setImageResource(R.drawable.pause); progressUpdateTimer = new Timer(); progressUpdateTimer.schedule(new ProgressDisplayTimerTask(milisecondsElapsed), 0, progressTimerInterval); } } public void onServiceDisconnected(ComponentName className) { new AlertDialog.Builder(NowPlayingActivity.this) .setMessage(getString(R.string.completed)) .setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface inteface, int button){ finish(); } }) .show(); } }; public void pauseClick(View v) { if(phService != null) { // Send pause. This Activity will receive a callback when the Power Hour's progression has // changed so will handle changing the UI accordingly in those callbacks phService.playPause(); } } public void skipClick(View v) { if(phService != null) { int nextID = phService.skip(); if(nextID < 0){ Toast.makeText(this, "Cannot skip last song", Toast.LENGTH_LONG).show(); } } } public void playlistClick(View v) { Intent launchPlaylistEditor = new Intent().setClass(this, PlaylistEditor.class); startActivity(launchPlaylistEditor); } void updateNowPlayingSongUI(int id){ PlaylistItem id3; try { id3 = MusicUtils.getInfoPack(this, id); } catch(Exception e) { // Signify error condition by assigning null to id3 as if song info couldn't be found id3 = null; } if(id3 == null) { Log.e("safeUpdateUI", "id3 is null!"); songText.setText("No track info"); albumText.setText(""); artistText.setText(""); } else { songText.setText(id3.song); albumText.setText(id3.album); artistText.setText(id3.artist); // Set the image art to the icon if there is no album art if(id3.art == null){ artView.setImageResource(R.drawable.bigicon); } else{ artView.setImageBitmap(id3.art); } } } void updateMinutesText(int currentMinute) { // Increase minute bc users don't like to count from 0 currentMinute++; if(currentMinute >= 10){ minutesText.setText("" + currentMinute); } else{ minutesText.setText("0" + currentMinute); } } void updateProgressBar(int miliseconds) { // Update the progress bar // Update the main progress bar to refelct the total progress int duration = new PreferenceRepository(this).getDuration() * 60000; double ratio = (double) miliseconds / (double) duration; pBar.setSecondaryProgress((int) (10000 * ratio)); // Update the secondary progress bar to reflect progress into the current minute ratio = (double) (miliseconds % 60000) / (double) 60000; pBar.setProgress((int) (ratio * 10000)); } public void onSongUpdate(int songID) { updateNowPlayingSongUI(songID); } public void onProgressUpdate(int currentMinute) { if(progressUpdateTimer != null) { progressUpdateTimer.cancel(); progressUpdateTimer.purge(); } progressUpdateTimer = new Timer(); progressUpdateTimer.schedule(new ProgressDisplayTimerTask(currentMinute * 60000), 0, progressTimerInterval); updateMinutesText(currentMinute); } @Override public void onProgressPaused() { // Cancel the intra-minute progress bar if(progressUpdateTimer != null) { progressUpdateTimer.cancel(); progressUpdateTimer.purge(); } // Set pause/play button icon to play pauseButton.setImageResource(R.drawable.play); } @Override public void onProgressResumed() { // Set pause/play button icon to pause pauseButton.setImageResource(R.drawable.pause); // Start a new intra-minute timer int milisecondsElapsed = phService.getProgress() * 1000; progressUpdateTimer = new Timer(); progressUpdateTimer.schedule(new ProgressDisplayTimerTask(milisecondsElapsed), 0, progressTimerInterval); } private class ProgressDisplayTimerTask extends TimerTask { private int miliseconds; private Handler handler; public ProgressDisplayTimerTask(int miliseconds) { this.miliseconds = miliseconds; this.handler = new Handler(); } @Override public void run() { miliseconds += progressTimerInterval; handler.post(new Runnable() { public void run() { NowPlayingActivity.this.updateProgressBar(miliseconds); } }); } } }