Android (Java) - What am I doing wrong?

Soldato
Joined
25 Sep 2003
Posts
3,725
Location
Manchester
I've stripped out the code I'm having issues with. Below I have the updateView which is static and below that I have a method
(spawnNotification) which takes a context (I've replaced the context I had with just the text CONTEXT). I sort of know what I need to do but after spending hours messing about with it, I've got nowhere. :o

Is there somewhere obvious I'm messing up?

TeaTimerMain.class

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


			TeaTimerMain.spawnNotification( CONTEXT );

			
		} 
	}

	
	@SuppressWarnings("deprecation")
	public static void spawnNotification(Context thisContext){
		NotificationManager nm = (NotificationManager) thisContext.getSystemService(Context.NOTIFICATION_SERVICE);
		@SuppressWarnings("deprecation")
		Notification notify = new Notification(android.R.drawable.stat_notify_more, "Finished", System.currentTimeMillis());
		Context context = thisContext;
		CharSequence title = "Title";
		CharSequence details = "details";
		Intent intent = new Intent(context, TeaTimerMain.class);
		PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
		notify.setLatestEventInfo(context, title, details, pending);
		nm.notify(0, notify);
	}
 
Last edited:
Why is updateView static and referencing textViewShowSeek (which one assumes isn't static)

UpdateView is static because the timer I'm using I could only get to work by having that as static.

I'm guessing the way I'm doing the timer is wrong.

Here's the timer code:

Code:
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!");
	}


}
 
If I remove static from the timer I get this :(

javaerror.jpg
 
The way the forum displays it is confusing. Care to put some more screenshots up?

I believe this line is pointless though:

Context context = thisContext;

As your are assigning thisContext to context, that is already of type Context?
 
Surely you want to instantiate the TeaTimerMain object rather than trying to call a static method? (I can't see all your code so it's hard to tell (and I don't do Android, just java). (or make the teatimer an object).
 
Last edited:
@GeX: I've seen that example before but I didn't follow it as I think it's outdated.

@nex20: Yup, that line is pointless actually, oops.

@Scougar: See, this is where I'm getting unstuck. My main is in TeaTimerMain so that runs at the start of course. Would I then make a TeaTimerMain object to work with but then that seems confusing because I have TeaTimerMain running already.

Here's all my code:

TeaTimerMain
Code:
package com.henleyb.teatimer;

import java.util.concurrent.TimeUnit;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaPlayer;
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;
	public static MediaPlayer playWhistle;
	private static long userTime;
	private MyCount newTimer;
	private ToggleButton brewButton;
	static Vibrator mVibrate;
	public static TeaReadyAlert bob;

	@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);

//		playWhistle.setDataSource(R.raw.whistle1);
//		playWhistle.setOnPreparedListener(this);
//		playWhistle.prepareAsync();

		// 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 the vibration
		mVibrate = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		bob = new TeaReadyAlert(getApplicationContext());

		// 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();
					mVibrate.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 void onPause() {
		super.onPause(); // Always call the superclass method first

	}

	@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 void onPrepared(MediaPlayer player) {
		playWhistle.start();
	}

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

			shaker();

			// Pop up box to be implemented
			// bob.show(this, "test");


			alertbox("Hello", "hello");

			
		} else {
			if (mVibrate != null) {
				mVibrate.cancel();
			}

			textViewShowSeek.setText(String
					.valueOf(textViewTime(millisUntilFinished)));
			seekBarTimerSet.setProgress((int) millisUntilFinished);
		}
	}

	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public static void shaker() {

		if (mVibrate.hasVibrator()) {
			System.out.println("has vibrator");
			mVibrate.vibrate(new long[] { 0, 500, 1000, 1000, 1000 }, 0);
		}

	}
	
	@SuppressWarnings("deprecation")
	
		protected void alertbox(String title, String mymessage)
		   {
		   new AlertDialog.Builder(this)
		      .setMessage(mymessage)
		      .setTitle(title)
		      .setCancelable(true)
		      .setNeutralButton(android.R.string.cancel,
		         new DialogInterface.OnClickListener() {
		         public void onClick(DialogInterface dialog, int whichButton){}
		         })
		      .show();
		   }
		// see http://androidsnippets.com/display-an-alert-box
	
}

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!");
	}


}

Thinking out loud; I guess maybe I should have main to set things up but then have another class for my updating. I guess it would then follow having an MVC type layout which would make sense more, leaving the TeaTimerMain to be the place where it creates the timer object and also an update view object? Would I then need handlers though? Aaargh!
 
Last edited:
After going away and having a breather from this I'm coming back to have another go. :D

I'm trying to figure out, using an Object orientated approach, how I should really be putting my Tea Timer together?

I have a timer object but should I also have a TeaTimerMain object that creates itself on the onCreate method? The TeaTimerMain object could then interact with the timer as neither would then be static? I'm struggling with the top down view of how it should be laid out and if my TeaTimerMain object should interrogate the timer (a while loop??) or if the timer should tell my main object when it's done (through onTick and onFinish etc)? What's the correct way that objects talk to each other and how to know if an object even exists for another one to talk too? :confused:

Thanks for the help so far, I don't think i'm a million miles off from being able to get this done but at the moment I've got a bit of a confusing mental block.
 
Last edited:
Not that I've done this before, but using an OO approach, your TeaTimer would be an object in itself, containing all the values and methods you need to make it would.

I think the issue here is that you are trying to code around errors (thanks to the 'helpful' IDE) rather than think about overall program design.
 
I always had trouble getting my head around OO, even while I was supposed to know a fair bit about Java. Once I returned to programming (specifically c#), I'm kinda getting the hang of it.

Instead of trying to get your head around the code you've already created, perhaps some OO examples might help you re-plan your solution? Although you may only ever spawn a single instance of your object, its always a good idea to avoid static unless you have a specific reason to. It would make things _much_ easier if you ever wanted to extend your program to use multiple instances, or even use those instances in a different program.

I always find the Car class analogy helpful, especially for things like Objects containing lists of other objects :)
 
Thanks guys, I missed that there were replies here. I learnt a lot with this app and it's called "Cuppa - Tea Timer" on Play if you wanted to see its final form! :) (https://play.google.com/store/apps/...51bGwsMSwyLDEsImNvbS5oZW5sZXliLnRlYXRpbWVyIl0.)

I agree that I should have gone about it differently. I sort of bumbled through that. I've learnt a lot more about objects now and how to use them better. I'm still confused by a lot of things, just waiting for something to click in my head but getting there!

I thought trying out lists would be a simple next step, turns out I find those damn confusing too! :mad:

I've bought the head first java book which seems pretty good. I'll stick with that and try to learn as much as I can using that and lynda.com as well as treehouse.com.
 
Last edited:
I don't find Android very OO friendly to be honest, and the layout of activity classes, especially the onCreate methods. It makes me feel dirty every time I write one.

I could just be doing it wrong, but then again I've yet to come across some decent books/guidelines. I think I've seen the official Android ones change about 3-4 times in the past 6 months. It seems even the big companies like google are sloppy as hell.
 
Back
Top Bottom