Search code examples
androidtimetimercountdowntimer

How to add a vibrations at specific intervals of a timer (java)


I currently have a functioning timer using a textview (if there is a similar/more efficient way to make a timer I'd love to know).

What I want to do is make the phone vibrate at specific intervals throughout the timer.

Example:

The timer starts at 10 minutes, and after 7 seconds it vibrates, then after 5 seconds, it vibrates then again after 7 seconds, and so on until the timer completes, where it will finish with a very long vibration.

Here is the code I have so far:

    public class RelaxActivity extends AppCompatActivity {

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }
    // declare variables for buttons and timer
    private static final long START_TIME_IN_MILLIS = 600000;

    private TextView mTextViewCountDown;
    private Button mButtonStartPause;
    private Button mButtonReset;

    

    private CountDownTimer mCountDownTimer;

    private boolean mTimerRunning;

    private long mTimerLeftInMillis = START_TIME_IN_MILLIS;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_relax);

        mTextViewCountDown = findViewById(R.id.text_view_countdown);

        mButtonStartPause = findViewById(R.id.button_start_stop);
        mButtonReset = findViewById(R.id.button_reset);

        mButtonStartPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mTimerRunning) {
                    pauseTimer();
                } else {
                    startTimer();
                }
            }
        });

        mButtonReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                resetTimer();
            }
        });

        updateCountDownText();

    }
    // start timer
    private void startTimer(){
        mCountDownTimer = new CountDownTimer(mTimerLeftInMillis, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                mTimerLeftInMillis = millisUntilFinished;
                updateCountDownText();
            }

            @Override
            public void onFinish() {
                mTimerRunning = false;
                mButtonStartPause.setText("Start");
                mButtonStartPause.setVisibility(View.INVISIBLE);
                mButtonReset.setVisibility(View.VISIBLE);
            }
        }.start();

        mTimerRunning = true;
        mButtonStartPause.setText("Pause");
        mButtonReset.setVisibility(View.INVISIBLE);
    }
    // cancel timer, change text of button to start, and make reset button visible
    private void pauseTimer(){
        mCountDownTimer.cancel();
        mTimerRunning = false;
        mButtonStartPause.setText("Start");
        mButtonReset.setVisibility(View.VISIBLE);
    }
    // reset timer method
    private void resetTimer(){
        mTimerLeftInMillis = START_TIME_IN_MILLIS;
        updateCountDownText();
        mButtonReset.setVisibility(View.INVISIBLE);
        mButtonStartPause.setVisibility(View.VISIBLE);
    }


    // do math for countdown text from milliseconds to minutes and then seconds
    private void updateCountDownText(){
        int minutes = (int) (mTimerLeftInMillis / 1000) / 60;
        int seconds = (int) (mTimerLeftInMillis / 1000) % 60;
        // convert minutes and seconds variables into time string
        String timeLeftFormatted = String.format(Locale.getDefault(),"%02d:%02d", minutes, seconds);

        mTextViewCountDown.setText(timeLeftFormatted);

    }
    // menu
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch(item.getItemId()) {
            case R.id.mnuMain:
                startActivity(new Intent(getApplicationContext(), MainMenuActivity.class));
                return true;
            case R.id.mnuExit:
                finish();
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

Solution

  • Use "Vibrator" and keep track of time in your timer's "onTick" method.

    Import Vibrator and VibrationEffect:

    import android.os.Vibrator;
    import android.os.VibrationEffect;
    

    Declare these two variables at the class level:

    private int secondCounter;
    private Vibrator vibrator;
    

    Initialize in onCreate:

    secondCounter = 0;
    vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    

    Increment secondCounter in your onTick method and check the count:

    public void onTick(long millisUntilFinished) {
        mTimerLeftInMillis = millisUntilFinished;
        updateCountDownText();
    
        // check the seconds that have passed
        if (secondCounter == 7 || secondCounter == 12) {
    
            // 500 is milliseconds of vibration, modify as needed
            vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
    
            // reset second counter
            if (secondCounter == 12) {
                secondCounter = -1;
            }
    
        }
    
        // increment secondCounter
        secondCounter++;
    }
    

    My logic for keeping track of the seconds might be off (didn't double check it), but you get the idea.

    For the vibration on when the timer is complete, just add another vibrator.vibrate call in the timer's onFinish method.

    Also, be sure to add the vibration permission in your app's manifest file.