Home > java > Review Request - Android CountDownTimer activity

Review Request - Android CountDownTimer activity

September 15Hits:1

I'm new to Java and Android. While I've created a project that seems to work, I'd like some opinions on how to make it better.

The app is going to be used as an interval workout timer/music player for Android devices. I know there's a ton of these already, but I thought it'd be a good way to learn the language, and it's useful for me. I haven't gotten the music player piece working yet. I was thinking of letting you select between selecting a playlist to play or using the 8tracks.com API to play streaming music. Comments are welcome, too.

The code can be seen in full in this Github.

Here's the main package:

package com.blueflamesys.workoutstopwatch;  import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.CountDownTimer; import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView;  public class WorkoutStopwatch extends Activity implements OnClickListener {  private static final String TAG = "WorkoutMain"; private static final String API_KEY = "30eee341eb88a9647b40d187faf508b50ca318d9"; private boolean timerRunning, isPaused = false; private long millisLeft = 0; private WorkoutTimer workoutTimer; private enum CounterState {     UNKNOWN(0),     WORKOUT(1),      REST(2);      private int stateValue;      CounterState(int state) {         this.stateValue = state;     }      public int getStateValue() {         return stateValue;     }      public void setStateValue(int state) {         this.stateValue = state;     }        } CounterState state = CounterState.WORKOUT;   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.workout_stopwatch);       // Set up OnClickListeners     ((Button) findViewById(R.id.start_button)).setOnClickListener(this);     ((Button) findViewById(R.id.pause_button)).setOnClickListener(this);     ((Button) findViewById(R.id.reset_button)).setOnClickListener(this);  }  @Override public void onClick(View v) {     switch(v.getId()) {     case R.id.start_button:         Log.d(TAG, "clicked on Start Button");         if (!isPaused && !timerRunning) {             Log.d(TAG, "Wasn't paused and wasn't running...");              timerRunning = true;              // If the state is unknown, set it to Workout first             int State = state.getStateValue();             if (State == 0) {                 state.setStateValue(1);             }             workoutTimer = new WorkoutTimer(50);             workoutTimer.start();          }           if (isPaused) {             timerRunning = true;             this.resumeTimer();             Log.d(TAG, "Was paused resumed timer...");         }          break;     case R.id.pause_button:         Log.d(TAG, "clicked on Pause Button");          if (timerRunning) {             timerRunning = false;             this.pauseTimer();             Log.d(TAG, "Paused timer...");         }          break;     case R.id.reset_button:          Log.d(TAG, "clicked on Reset Button");          if (!timerRunning) {             Log.d(TAG, "Wasn't running...");             isPaused = false;             TextView digital_display = (TextView) findViewById(R.id.digital_display);             digital_display.setText("00:00.0");         }         break;     } }  @Override public boolean onCreateOptionsMenu(Menu menu) {     super.onCreateOptionsMenu(menu);     MenuInflater inflater = getMenuInflater();     inflater.inflate(R.menu.stopwatch_settings, menu);     return true; }  @Override public boolean onOptionsItemSelected(MenuItem item) {     switch (item.getItemId()) {     case R.id.settings:         startActivity(new Intent(this, Prefs.class));         return true;     case R.id.about:         Intent i = new Intent(this, About.class);         startActivity(i);         break;     } return false; }  public void pauseTimer() {     isPaused = true;     SharedPreferences workoutTimerDB = getSharedPreferences("workoutTimerDB", MODE_PRIVATE);     SharedPreferences.Editor edit = workoutTimerDB.edit();     edit.putLong("millisLeft", (Long)this.millisLeft);     edit.commit();     workoutTimer.cancel(); }  public void resumeTimer() {     if (isPaused) {         isPaused = false;         SharedPreferences workoutTimerDB = getSharedPreferences("workoutTimerDB", MODE_PRIVATE);         millisLeft = workoutTimerDB.getLong("millisLeft", 0);         workoutTimer = new WorkoutTimer(millisLeft, 50);         workoutTimer.start();     } }  private class WorkoutTimer extends CountDownTimer{     public WorkoutTimer(long interval) {         super(getThisTime(), interval);         Log.d(TAG, "WorkoutTimer Constructed with interval only...");     }      public WorkoutTimer(long millis, long interval) {         super(millis, interval);         Log.d(TAG, "WorkoutTimer Constructed with both...");     }      public void onFinish() {         int State = state.getStateValue();         int roundsLeft = 0;          if (State == 1) {             state.setStateValue(2);         } else {             state.setStateValue(1);             decrementRounds();         }          Log.d(TAG, "WorkoutState = " + state.getStateValue());          try {             TextView numOfRounds = (TextView) findViewById(R.id.number_of_rounds);              roundsLeft = Integer.parseInt(numOfRounds.getText().toString());         } catch(NumberFormatException nfe) {             roundsLeft = 999;         }          if (roundsLeft > 0) { // If there's still rounds, start the timer.             workoutTimer = new WorkoutTimer(100);             workoutTimer.start();         } else { // Stop the timer and reset the display             workoutTimer.cancel();              TextView digital_display = (TextView) findViewById(R.id.digital_display);             digital_display.setText("00:00.0");         }     }      public void onTick(long millisUntilFinished) {         final long minutes_left = ((millisUntilFinished / 1000) / 60);         final long seconds_left = (millisUntilFinished / 1000) - (minutes_left * 60);         final long millis = millisUntilFinished % 100;         final String time_left = String.format("%02d:%02d.%01d", minutes_left, seconds_left,                  millis/10);          TextView digital_display = (TextView) findViewById(R.id.digital_display);         digital_display.setText(time_left);         millisLeft = millisUntilFinished;     }      @Override     protected void finalize() throws Throwable {         super.finalize();         Log.d(TAG, "Object destroyed");     } }  private long getThisTime() {     long time = 0;      TextView workout_time = (TextView) findViewById(R.id.workout_time);     TextView rest_time = (TextView) findViewById(R.id.rest_time);      switch(state.getStateValue()) {     case 1:         try {                 time = Integer.parseInt(workout_time.getText().toString());             } catch(NumberFormatException nfe) {                 time = 999;             }          Log.d(TAG, "Workout time = " + time);         break;     case 2:         try {                 time = Integer.parseInt(rest_time.getText().toString());             } catch(NumberFormatException nfe) {                 time = 999;             }          Log.d(TAG, "Rest time = " + time);         break;     case 0:         time = 0;         break;     }     return time * 1000; }  private void decrementRounds() {     int roundsLeft = 2;      TextView numOfRounds = (TextView) findViewById(R.id.number_of_rounds);      try {         roundsLeft = Integer.parseInt(numOfRounds.getText().toString());     } catch(NumberFormatException nfe) {         roundsLeft = 999;     }      roundsLeft -= 1;      numOfRounds.setText(String.valueOf(roundsLeft));     Log.d(TAG, "set rounds to = " + roundsLeft); }  } 

Don't hold back, and feel free to ask any questions or give any criticisms.


Your usage of enums

You are misusing your enums. I would get rid of the getStateValue and setStateValue methods. The enum itself is a state.

I would change code that looks like this:

        int State = state.getStateValue();
        if (State == 0) {

To this:

        if (state == UNKNOWN) {

What your original code here actually does is that it's taking the state value, which you initialize to CounterState.WORKOUT and then it's changing the internal integer value stored in the enum. You never change your state variable from WORKOUT, but instead you change the integer value of the WORKOUT-state. That's definitely not how enums should be used.

(You're also using the variable name State which defies Java coding conventions that variable names should start with a lower case letter)

So, by changing the code snippet in the way I suggested above you will get rid of a lot of the "magic numbers" 0, 1 and 2 by using your enums properly. You will also improve readability. You won't have to remember "Which state is 2" anymore.

I would also mark the enum type itself with static like this: private static enum CounterState { because the CounterState is not dependent on anything in your outer class). I would also mark its constructor with private - or rather, I would get rid of the constructor entirely. There is no use for it when you remove the getStateValue and setStateValue methods. You can use state.ordinal() if you really want to access its int value (which you should generally try to avoid but is useful for things like serialization). The ordinal() method returns the index of the enum.

Other variables

I would rename your isPaused to simply paused. isPaused is a naming convention for a method that returns your internal paused boolean value. You also have one boolean for if the timer is running (timerRunning) and one for if the timer is paused. This confuses me a lot. If the timer isn't running, then it's paused isn't it? Or should the possible options be STOPPED, PAUSED, RUNNING? In that case make it an enum (without any getters and setters!).

On click

I also agree with @thepoosh about defining onClick for your buttons in the XML, unless you're targeting Android API < 4 (which no-one really uses anymore). For information about using onClick in your XML, see the Android documentation.

from the little I saw I have just a few comments (the code looks fairly fine)

  1. if all you do with the buttons is set the onClickListener there is no reason not to use the built in xml attribute onClick when designing the buttons.
  2. when editing or reading from SharedPrefrences you should probably synchronize it like this:
    public static final String WORKOUT_SHARED_PREFS = "workoutTimerDB";
    public void pauseTimer() {
    isPaused = true;
    synchronized(WORKOUT_SHARED_PREFS) {
            SharedPreferences workoutTimerDB = getSharedPreferences(WORKOUT_SHARED_PREFS, MODE_PRIVATE);
            SharedPreferences.Editor edit = workoutTimerDB.edit();
            edit.putLong("millisLeft", (Long)this.millisLeft);

Related Articles

  • Review Request - Android CountDownTimer activitySeptember 15

    I'm new to Java and Android. While I've created a project that seems to work, I'd like some opinions on how to make it better. The app is going to be used as an interval workout timer/music player for Android devices. I know there's a ton of these al

  • Appcelerator Could not find method android.app.Activity.checkSelfPermission

    Appcelerator Could not find method android.app.Activity.checkSelfPermissionFebruary 5

    I cant seem to get geo location to work on Android just seem to get the following bug. [INFO] : dalvikvm: Could not find method android.app.Activity.checkSelfPermission, referenced from method ti.modules.titanium.geolocation.GeolocationModule.hasLoca

  • NullPointerException in android.app.Activity.onCreate(android/app/Activity.java:874)July 22

    I'm trying to get libGDX 1.6.1 to run on Android via Ruboto. So far, I: Generated a new libGDX app using the app generator Got the desktop app to run (Java) Got the android app to run (Java) Converted the desktop app to run with JRuby Got a basic Rub

  • Http request in the activity and pass the result to fragments in a ViewPagerJanuary 18

    I have to make an async http request in my activity and display the results in the two fragments that are added to a ViewPager using PagerAdapter. How can I do this considering the ViewPager and Fragments can be created before the http request is fin

  • Android: finish() Activity Runtime ExceptionJanuary 21

    I am closing an activity via finish(). It works fine on several devices but on a Samsung Galaxy S3 Neo running Android 4.4 I get the following issue: java.lang.RuntimeException android.app.ActivityThread.performDestroyActivity(ActivityThread.java:370

  • Requesting Android M permissions from Activity with noHistory="true" and/or showOnLockScreen="true"February 2

    I'm working on a video calling app and I have an 'incoming call' screen which alerts the user when someone is calling them. This screen is an Activity triggered by an incoming GCM and has noHistory="true" and showOnLockScreen="true" se

  • android switch activity with CountDownTimerJanuary 22

    i am having 2 activities right now, the first one contain a "start" button, and when user press start, it will go to the second activity by Intent myIntent = new Intent(getApplicationContext(), MainActivity.class); startActivity(myIntent); overr

  • Android "Search" Activity and corresponding XMLAugust 27

    Below is an Android Activity and corresponding XML layout. The function of the class is to search an SQLite database to show results to the user. Can I get a review of the code quality? This is my first Android app so feedback would be greatly apprec

  • High speed Capture request Android MarshmallowJanuary 27

    I'm having some issues setting up a capture request for high speed video. I intend to capture at 120 FPS on Nexus 6P. I set the minimum API to 23, since I don't plan to deploy this app to any other phone. Where I'm not quite understanding is how to g

  • Android - CountDownTimer on DialogFragmentFebruary 7

    So, I created a create a button the will pop up an alert dialog with radio button items are 5 , 10 , 15 mins and when I select the item the countdowntimer will start based on the mins selected and my problem now is how to stop the timer with another

  • Android Fragmnet activity from Fragment.Noe in Fragment Activity getting nullpointer exception on getActionBar() February 14

    i want to work with action bar in android. for this i use getActionBar().setHomeButtonEnabled(true); below the SetContentView . but is gives me null pointer exception. when i try only getActionBar() it works fine. please help me. --------------Soluti

  • Allow user to request resend of activation emailApril 22

    I would like to code a way for a user to request the activation email be resent. Is there a function I can call from within a form-submit hook that will accomplish this? (using Drupal 7) --------------Solutions------------- First you need to create a

  • Kings Drinking Game Review Request 2.0 JForm GUI

    Kings Drinking Game Review Request 2.0 JForm GUIOctober 6

    This request is a new version of this request: Kings Drinking Game This version has a custom JForm GUI and almost all new methods. GUI: I wrote a program to simulate the drinking card game Kings, now with a JFrom GUI. The GUI is also now not hard-cod

  • Which text is more effective to encourage users to leave review for Android AppApril 21

    I have prepared 2 types of text which used to ask user to leave review in Google Play store for Android app. It is a dialog, which will pop up, if user has used this app for N times for last M days. First Text Hello! My name is xxx. I'm the solo deve

  • Does Android keep activity logs or save keystrokes?August 10

    I want to know whether Android phones records activity logs of what I have done in a whole day. I've already checked that there is no Carrier IQ. Is this true that after switching off the phone all activity logs vanish? Does my Android phone keep sav

  • Sharepoint 2013 workflow REST: service response on filter request with httpSend activityOctober 17

    in a declarative / visual studio workflow we want to filter a list and use a httpSend activity with http://mysite/_api/web/lists/GetByTitle('myList')/items?$filter=Title eq 'customer' for the uri request parameter. Result = no item. Putting this URL

  • Get IO stats request sql like activity monitor

    Get IO stats request sql like activity monitorApril 22

    Is it possible to get the same value as in the activity monitor (SSMS) for the "Database IO" as a result of a SQL request? --------------Solutions------------- You can use a query like this to calculate read/write rates and latency (though due t

  • Android One Activity Transition Different Views

    Android One Activity Transition Different ViewsJanuary 16

    I was wondering if someone can advise what the best way to do the following: I'm creating an application where I'm asking the user a question and the user either answers the question or proceeds to the next question with fade in and fade out animatio

  • How to create service android without ActivityJanuary 18

    I need to implement a service in android this: 1. Activity should not have, anything visual. 2. Once you install it, is running in the background. They recommend me to use? regards Alex

  • Android multiple activity instances in memory snapshot on screen rotation changeJanuary 19

    I created a simple Blank Activity (MainActivity class) in Android Studio and ran it on my Samsung phone. I rotated the screen several times from portrait to landscape mode. I took a memory snapshot and discovered that there were several instances of

Copyright (C) 2017 ceus-now.com, All Rights Reserved. webmaster#ceus-now.com 14 q. 0.605 s.