Android (Java) and the correct way to go about this:

Soldato
Joined
25 Sep 2003
Posts
3,725
Location
Manchester
I've built a simple tea timer which uses a timer to count down from a time the user inputs.

I'm trying to learn more about coding so I made a timer object and it works and prints out on my log that it's counting down from the value the user sets, but the issue I'm having is that because it's no longer in my main class, I can't link it up with a TextView to show the remaining time.

How to get the timer object I'm creating to send its time to a TextView on every tick, that's in my main class?

I'm wondering if I need to create getters and setters or something? :confused:
 
Associate
Joined
20 Jan 2013
Posts
280
Location
Shropshire
I haven't developed any Android and have only been using Java for around a year but hope it might help.

I take it on the Timer object you have a method or such to calculate the remaining time left?

is the TextView an object as well or is that just the android screen/view?

once you've created a new instance of Timer (when you load the app?) simply call a getRemaingTime() method or something similar on the screen?
 
Soldato
OP
Joined
25 Sep 2003
Posts
3,725
Location
Manchester
Yeah, the timer has a method onTick, in fact - here's all my code which might make things a bit easier! :)

A TextView is basically a label on screen for text output. Mine is called textViewShowSeek.

I did think I might be able to do something like newTimer.onTick in the main class and then use that to update the TextView but I haven't got it working.

My Main Class
Code:
	package com.henleyb.teatimer;

import java.util.concurrent.TimeUnit;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class TeaTimerMain extends Activity implements OnSeekBarChangeListener {

	public TextView textViewShowSeek;
	SeekBar seekBarTimerSet;
	public long mInitAmount = 180000;
	public long mSeekBarAmount = 2000;
	MediaPlayer playWhistle;
	public MyCount newTimer;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main_tea_timer);
		seekBarTimerSet = (SeekBar) findViewById(R.id.seekBarTimerSet);
		textViewShowSeek = (TextView) findViewById(R.id.tvShowSeek);
		playWhistle = MediaPlayer.create(this, R.raw.whistle1);
		seekBarTimerSet.setOnSeekBarChangeListener(this);
		

	}

	@Override
	public boolean onCreateOptionsMenu(android.view.Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater blowUp = getMenuInflater();
		blowUp.inflate(R.menu.tea_settings_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.aboutUs:
			Intent i = new Intent("com.henleyb.teatimer.ABOUT");
			startActivity(i);
			break;
		}
		return false;
	}

	@SuppressLint("DefaultLocale")
	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		if (fromUser == true) {
			mSeekBarAmount = progress;
			textViewShowSeek.setText(textViewTime(progress));
		}
	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		if (newTimer != null) {
			newTimer.cancel();
		}
		;
	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		newTimer = new MyCount(mSeekBarAmount, 1000);
		newTimer.start();

	}

	// converts milliseconds to time for display
	@SuppressLint("DefaultLocale")
	private String textViewTime(long convertVar) {
		String formattedVar;
		formattedVar = String.format(
				"%d min, %d sec",
				TimeUnit.MILLISECONDS.toMinutes(convertVar),
				TimeUnit.MILLISECONDS.toSeconds(convertVar)
						- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS
								.toMinutes(convertVar)));
		return formattedVar;
	}
}

My Timer Class
Code:
package com.henleyb.teatimer;

import android.os.CountDownTimer;

// countdowntimer is an abstract class, so extend it and fill in methods
public class MyCount extends CountDownTimer {

	public MyCount(long millisInFuture, long countDownInterval) {
		super(millisInFuture, countDownInterval);
	}
	
	@Override
	public void onFinish() {
		System.out.println("FINISHED!");
	}

	@Override
	public void onTick(long millisUntilFinished) {
		// textViewShowSeek.setText(textViewTime(millisUntilFinished));
		System.out.println("TICK!");
	}


}


Also, as it's the timer that ticks, would that mean it has to do the updating or is there a way to monitor the timer and react to it by updating the TextView?
 
Last edited:

GeX

GeX

Soldato
Joined
17 Dec 2002
Posts
6,895
Location
Manchester
Create a public method in your main class to allow the textview to be updated?

Code:
public void updateView(long time)
{
  textview.setText(time)
}

and in your timer

Code:
	@Override
	public void onTick(long millisUntilFinished) {
                TeaTimerMain.updateView(millisUntilFinished);
		System.out.println("TICK!");
	}

or something to that effect
 
Last edited:
Soldato
OP
Joined
25 Sep 2003
Posts
3,725
Location
Manchester
Thanks GeX, I did something similar. I just literally put (in my MyCount onTick)

TeaTimerMain.textViewShowSeek.setText(newTime);

which works but I didn't know if it was the 'proper' way of doing it as it doesn't seem very object orientated friendly I guess.
 

GeX

GeX

Soldato
Joined
17 Dec 2002
Posts
6,895
Location
Manchester
If he were writing his own countdown timer then he might, but he's using CountDownTimer... so I don't think AysncTask is going to be any use here.
 
Soldato
OP
Joined
25 Sep 2003
Posts
3,725
Location
Manchester
I redid a few things in my code. It's probably still a complete mess to anyone who knows much about proper coding but hey, it's working. :D

I thought I'd try and be clever and introduce a vibrate for when the timer ends. Adding one in to my main class throws up the error: Cannot make a static reference to the non-static method shaker(int) from the type TeaTimerMain.

I've looked in to how static and non static things work and my understanding is that you can't have a static reference to an object since it may not yet exist but you can to the class. I've played around but I'm not sure what i'm doing wtong. I end up going in loops trying to create new objects and things to take care of the vibrate but then end up with more issues. What do I need to read up on to figure this out?


Main Class
Code:
package com.henleyb.teatimer;

import java.util.concurrent.TimeUnit;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

public class TeaTimerMain extends Activity implements OnSeekBarChangeListener {

	private static TextView textViewShowSeek;
	private static SeekBar seekBarTimerSet;
	// MediaPlayer playWhistle;
	private static long userTime;
	private MyCount newTimer;
	private ToggleButton brewButton;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main_tea_timer);
		brewButton = (ToggleButton) findViewById(R.id.butGo);
		seekBarTimerSet = (SeekBar) findViewById(R.id.seekBarTimerSet);
		textViewShowSeek = (TextView) findViewById(R.id.tvShowSeek);
		// playWhistle = MediaPlayer.create(this, R.raw.whistle1);
		seekBarTimerSet.setOnSeekBarChangeListener(this);

		// Set up and get the users preference for brew time.
		SharedPreferences userDetails = this.getSharedPreferences(
				"userdetails", MODE_PRIVATE);
		userTime = userDetails.getLong("userTime", 2000);
		updateView(userTime);


		
		// Set up Toggle Button listener and logic
		brewButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {

				if (isChecked) {
					// Toggle light is off

					Log.d("ToggleCheck", "Toggle off");
					newTimer.cancel();

				} else {
					// Toggle light is on
					Log.d("ToggleCheck", "Toggle on");
					timerInit(userTime);

				}
			}

		});

		// Fire up the timer for the first launch.
		timerInit(userTime);

	}

	@Override
	public boolean onCreateOptionsMenu(android.view.Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater blowUp = getMenuInflater();
		blowUp.inflate(R.menu.tea_settings_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.aboutUs:
			Intent i = new Intent("com.henleyb.teatimer.ABOUT");
			startActivity(i);
			break;
		case R.id.savePrefs:
			TeaTimerMain.setDefaults(this);
			break;
		}
		return false;
	}

	@SuppressLint("DefaultLocale")
	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		if (fromUser == true) {
			userTime = progress;
			updateView(progress);
			System.out.println("Progress Changed, userTime: " + userTime);
		}

	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		if (newTimer != null) {
			newTimer.cancel();
			brewButton.setChecked(true);
		}
	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		newTimer = new MyCount(userTime, 1000);

	}

	public void timerInit(Long mSeekBarAmount) {
		if (newTimer != null && brewButton.isChecked()) {
			newTimer.cancel();
		} else {
			newTimer = new MyCount(mSeekBarAmount, 1000);
			newTimer.start();
		}
	}

	public static  void setDefaults(Context userPrefs) {
		// fired by button on the Main View

		SharedPreferences userDetails = userPrefs.getSharedPreferences(
				"userdetails", MODE_PRIVATE);
		Editor edit = userDetails.edit();
		edit.clear();
		edit.putLong("userTime", userTime);
		// edit.putString("password", txtPass.getText().toString().trim());
		edit.commit();
		Toast.makeText(
				userPrefs,
				"Your ideal brew time of " + textViewTime(userTime)
						+ " has been saved.", Toast.LENGTH_LONG).show();
	}

	// converts milliseconds to time for display
	@SuppressLint("DefaultLocale")
	static String textViewTime(long convertVar) {
		String formattedVar;
		formattedVar = String.format(
				"%d min %d sec",
				TimeUnit.MILLISECONDS.toMinutes(convertVar),
				TimeUnit.MILLISECONDS.toSeconds(convertVar)
						- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS
								.toMinutes(convertVar)));
		return formattedVar;
	}

	public static void updateView(long millisUntilFinished) {
		if (millisUntilFinished == 0) {
			textViewShowSeek.setText("Finished!");
			
			shaker(500);
			

		} else {
			textViewShowSeek.setText(String
					.valueOf(textViewTime(millisUntilFinished)));
			seekBarTimerSet.setProgress((int) millisUntilFinished);
		}
	}
	
	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public void shaker(int amount) {
	
		Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		v.vibrate(amount);

		if (v.hasVibrator()) {
			System.out.println("has vibrator");
		}

	}

}

Timer Class
Code:
package com.henleyb.teatimer;

import android.os.CountDownTimer;

// countdowntimer is an abstract class, so extend it and fill in methods
public class MyCount extends CountDownTimer {

	public MyCount(long millisInFuture, long countDownInterval) {
		super(millisInFuture, countDownInterval);
	}

	@Override
	public void onFinish() {
		System.out.println("FINISHED!");
		 TeaTimerMain.updateView(0);
	}

	@Override
	public void onTick(long millisUntilFinished) {
		TeaTimerMain.updateView(millisUntilFinished);
		System.out.println("TICK!");
	}


}
 
Last edited:
Back
Top Bottom