/* * Copyright (c) 2014. Marshal Chen. */ package com.marshalchen.common.demoofui.standUpTimer; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import com.marshalchen.common.demoofui.R; import com.marshalchen.common.usefulModule.standuptimer.model.Meeting; import com.marshalchen.common.usefulModule.standuptimer.model.Team; import com.marshalchen.common.usefulModule.standuptimer.utils.Logger; import com.marshalchen.common.usefulModule.standuptimer.utils.TimeFormatHelper; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class StandupTimer extends Activity implements OnClickListener { protected static final String REMAINING_INDIVIDUAL_SECONDS = "remainingIndividualSeconds"; protected static final String REMAINING_MEETING_SECONDS = "remainingMeetingSeconds"; protected static final String STARTING_INDIVIDUAL_SECONDS = "startingIndividualSeconds"; protected static final String COMPLETED_PARTICIPANTS = "completedParticipants"; protected static final String TOTAL_PARTICIPANTS = "totalParticipants"; protected static final String CURRENT_INDIVIDUAL_STATUS_SECONDS = "currentIndividualStatusSeconds"; protected static final String MEETING_START_TIME = "meetingStartTime"; protected static final String INDIVIDUAL_STATUS_END_TIME = "individualStatusEndTime"; protected static final String QUICKEST_STATUS = "quickestStatus"; protected static final String LONGEST_STATUS = "longestStatus"; private int currentIndividualStatusSeconds = 0; private int remainingIndividualSeconds = 0; private int remainingMeetingSeconds = 0; private int startingIndividualSeconds = 0; private int completedParticipants = 0; private int totalParticipants = 0; private int warningTime = 0; private boolean finished = false; private Timer timer = null; private PowerManager.WakeLock wakeLock = null; private Team team = null; private long meetingStartTime = 0; private long individualStatusStartTime = 0; private long individualStatusEndTime = 0; private int quickestStatus = Integer.MAX_VALUE; private int longestStatus = 0; private static MediaPlayer bell = null; private static MediaPlayer airhorn = null; private Handler updateDisplayHandler = new Handler() { @Override public void handleMessage(Message msg) { updateDisplay(); } }; private Handler disableIndividualTimerHandler = new Handler() { @Override public void handleMessage(Message msg) { disableIndividualTimer(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.stand_up_timer_timer); team = Team.findByName(getIntent().getStringExtra("teamName"), this); if (team != null) { Logger.i("Starting new meeting for team '" + team.getName() + "'"); } setVolumeControlStream(AudioManager.STREAM_MUSIC); initializeSounds(); initializeButtonListeners(); initializeTimerValues(); updateDisplay(); } @Override protected void onResume() { super.onResume(); acquireWakeLock(); startTimer(); } @Override protected void onPause() { super.onPause(); synchronized(this) { cancelTimer(); releaseWakeLock(); if (finished) { clearState(); } else { saveState(); } } } @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { Logger.i("Key pressed: " + keyCode); if (keyCode == KeyEvent.KEYCODE_BACK) { // Shutdown the timer when back is pressed. Let the framework // handle the back behavior. shutdownTimer(); } return super.onKeyDown(keyCode, keyEvent); } public void onClick(View v) { switch (v.getId()) { case R.id.next_button: processNextButtonClick(); break; case R.id.finished_button: processFinishedButtonClick(); break; } } private void initializeSounds() { if (bell == null) { Logger.d("Loading the bell sound"); bell = MediaPlayer.create(this, R.raw.bell); } if (airhorn == null) { Logger.d("Loading the airhorn sound"); airhorn = MediaPlayer.create(this, R.raw.airhorn); } } private void initializeButtonListeners() { View nextButton = findViewById(R.id.next_button); nextButton.setOnClickListener(this); View finishedButton = findViewById(R.id.finished_button); finishedButton.setOnClickListener(this); } private void initializeTimerValues() { int meetingLength = getIntent().getIntExtra("meetingLength", 0); int numParticipants = getIntent().getIntExtra("numParticipants", 0); Logger.d("Data from Intent: meetingLength = " + meetingLength); Logger.d("Data from Intent: numParticipants = " + numParticipants); loadState(meetingLength, numParticipants); } protected synchronized void updateDisplay() { if (individualStatusInProgress()) { TextView individualTimeRemaining = (TextView) findViewById(R.id.individual_time_remaining); individualTimeRemaining.setText(TimeFormatHelper.formatTime(remainingIndividualSeconds)); individualTimeRemaining.setTextColor(TimeFormatHelper.determineColor(remainingIndividualSeconds, warningTime)); TextView participantNumber = (TextView) findViewById(R.id.participant_number); participantNumber.setText("participant" + " " + (completedParticipants + 1) + "/" + totalParticipants); } else { disableIndividualTimer(); } TextView totalTimeRemaining = (TextView) findViewById(R.id.total_time_remaining); totalTimeRemaining.setText(TimeFormatHelper.formatTime(remainingMeetingSeconds)); totalTimeRemaining.setTextColor(TimeFormatHelper.determineColor(remainingMeetingSeconds, warningTime)); } private synchronized void startTimer() { Logger.d("Starting a new timer"); timer = new Timer(); TimerTask updateTimerValuesTask = new TimerTask() { @Override public void run() { updateTimerValues(); } }; timer.schedule(updateTimerValuesTask, 1000, 1000); } private synchronized void cancelTimer() { if (timer != null) { Logger.d("Canceling timer"); timer.cancel(); timer = null; } } protected synchronized void updateTimerValues() { currentIndividualStatusSeconds++; if (remainingIndividualSeconds > 0) { remainingIndividualSeconds--; if (remainingIndividualSeconds == warningTime) { Logger.d("Playing the bell sound"); if (Prefs.playSounds(this)) { playWarningSound(); } } else if (remainingIndividualSeconds == 0) { Logger.d("Playing the airhorn sound"); if (Prefs.playSounds(this)) { playFinishedSound(); } } } if (remainingMeetingSeconds > 0) remainingMeetingSeconds--; updateDisplayHandler.sendEmptyMessage(0); } private synchronized void processNextButtonClick() { completedParticipants++; calculateIndividualStatusStats(); if (completedParticipants == totalParticipants) { Logger.d("Individual status complete"); individualStatusEndTime = System.currentTimeMillis(); disableIndividualTimerHandler.sendEmptyMessage(0); } else { Logger.d("Setting up the next participant"); if (startingIndividualSeconds < remainingMeetingSeconds) { remainingIndividualSeconds = startingIndividualSeconds; } else { remainingIndividualSeconds = remainingMeetingSeconds; } updateDisplay(); } } private synchronized void calculateIndividualStatusStats() { if (currentIndividualStatusSeconds > longestStatus) { longestStatus = currentIndividualStatusSeconds; } if (currentIndividualStatusSeconds < quickestStatus) { quickestStatus = currentIndividualStatusSeconds; } currentIndividualStatusSeconds = 0; } protected synchronized void disableIndividualTimer() { Logger.d("Disabling the individual timer"); remainingIndividualSeconds = 0; TextView participantNumber = (TextView) findViewById(R.id.participant_number); participantNumber.setText("individual_status_complete"); TextView individualTimeRemaining = (TextView) findViewById(R.id.individual_time_remaining); individualTimeRemaining.setText(TimeFormatHelper.formatTime(remainingIndividualSeconds)); individualTimeRemaining.setTextColor(Color.GRAY); Button nextButton = (Button) findViewById(R.id.next_button); nextButton.setClickable(false); nextButton.setTextColor(Color.GRAY); } private void acquireWakeLock() { if (wakeLock == null) { Logger.d("Acquiring wake lock"); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass().getCanonicalName()); wakeLock.acquire(); } } private void releaseWakeLock() { if (wakeLock != null && wakeLock.isHeld()) { Logger.d("Releasing wake lock"); wakeLock.release(); wakeLock = null; } } protected synchronized void loadState(int meetingLength, int numParticipants) { warningTime = Prefs.getWarningTime(this); SharedPreferences preferences = getPreferences(MODE_PRIVATE); totalParticipants = preferences.getInt(TOTAL_PARTICIPANTS, numParticipants); remainingMeetingSeconds = preferences.getInt(REMAINING_MEETING_SECONDS, (meetingLength * 60)); startingIndividualSeconds = preferences.getInt(STARTING_INDIVIDUAL_SECONDS, remainingMeetingSeconds / totalParticipants); remainingIndividualSeconds = preferences.getInt(REMAINING_INDIVIDUAL_SECONDS, startingIndividualSeconds); completedParticipants = preferences.getInt(COMPLETED_PARTICIPANTS, 0); currentIndividualStatusSeconds = preferences.getInt(CURRENT_INDIVIDUAL_STATUS_SECONDS, 0); meetingStartTime = preferences.getLong(MEETING_START_TIME, System.currentTimeMillis()); individualStatusEndTime = preferences.getLong(INDIVIDUAL_STATUS_END_TIME, 0); quickestStatus = preferences.getInt(QUICKEST_STATUS, Integer.MAX_VALUE); longestStatus = preferences.getInt(LONGEST_STATUS, 0); team = Team.findByName(getIntent().getStringExtra("teamName"), this); individualStatusStartTime = meetingStartTime; } private synchronized void saveState() { SharedPreferences.Editor preferences = getPreferences(MODE_PRIVATE).edit(); preferences.putInt(REMAINING_INDIVIDUAL_SECONDS, remainingIndividualSeconds); preferences.putInt(REMAINING_MEETING_SECONDS, remainingMeetingSeconds); preferences.putInt(STARTING_INDIVIDUAL_SECONDS, startingIndividualSeconds); preferences.putInt(COMPLETED_PARTICIPANTS, completedParticipants); preferences.putInt(TOTAL_PARTICIPANTS, totalParticipants); preferences.putInt(CURRENT_INDIVIDUAL_STATUS_SECONDS, currentIndividualStatusSeconds); preferences.putLong(MEETING_START_TIME, meetingStartTime); preferences.putLong(INDIVIDUAL_STATUS_END_TIME, individualStatusEndTime); preferences.putInt(QUICKEST_STATUS, quickestStatus); preferences.putInt(LONGEST_STATUS, longestStatus); preferences.commit(); } private void clearState() { getPreferences(MODE_PRIVATE).edit().clear().commit(); } private synchronized boolean individualStatusInProgress() { return completedParticipants < totalParticipants; } private synchronized void processFinishedButtonClick() { if (completedParticipants != totalParticipants) { completedParticipants++; calculateIndividualStatusStats(); } shutdownTimer(); storeMeetingStats(); finish(); } private synchronized void shutdownTimer() { destroySounds(); finished = true; } private static void destroySounds() { bell.stop(); bell.release(); bell = null; airhorn.stop(); airhorn.release(); airhorn = null; } protected void playWarningSound() { playSound(bell); } protected void playFinishedSound() { playSound(airhorn); } private void playSound(MediaPlayer mp) { mp.seekTo(0); mp.start(); } private void storeMeetingStats() { if (team != null) { long meetingEndTime = System.currentTimeMillis(); if (individualStatusEndTime == 0) { individualStatusEndTime = meetingEndTime; } try { Meeting meeting = new Meeting(team, new Date(meetingStartTime), completedParticipants, (int)((individualStatusEndTime - individualStatusStartTime) / 1000), (int)((meetingEndTime - meetingStartTime) / 1000), quickestStatus, longestStatus); persistMeeting(meeting); } catch (IllegalArgumentException e) { Logger.e("Could not store the meeting in the database. " + e); } } } protected void persistMeeting(Meeting meeting) { meeting.save(this); } protected synchronized int getRemainingIndividualSeconds() { return remainingIndividualSeconds; } protected synchronized int getRemainingMeetingSeconds() { return remainingMeetingSeconds; } protected synchronized int getStartingIndividualSeconds() { return startingIndividualSeconds; } protected synchronized int getCompletedParticipants() { return completedParticipants; } protected synchronized int getTotalParticipants() { return totalParticipants; } protected synchronized int getWarningTime() { return warningTime; } protected synchronized boolean isFinished() { return finished; } protected synchronized Timer getTimer() { return timer; } protected synchronized PowerManager.WakeLock getWakeLock() { return wakeLock; } protected synchronized Team getTeam() { return team; } protected synchronized void setTeam(Team team) { this.team = team; } protected synchronized long getMeetingStartTime() { return meetingStartTime; } protected synchronized long getIndividualStatusStartTime() { return individualStatusStartTime; } protected synchronized long getIndividualStatusEndTime() { return individualStatusEndTime; } protected synchronized int getQuickestStatus() { return quickestStatus; } protected synchronized int getLongestStatus() { return longestStatus; } protected synchronized int getCurrentIndividualStatusSeconds() { return currentIndividualStatusSeconds; } }