Timer with Pause and Resume Support – Android/Java

The CountDownTimer implementation of Android may not be suitable for all cases as the onTick() method of the CountDownTimer runs on the main/UI thread.

The same could also be achieved using a TimerTask, but we do not have support for Pause/Resume operations when you’re using a Timer and a TimerTask.

The following implementation of a generic Timer runs on a separate thread and hence is most suitable for any operation that does not involve UI updates. It is basically a wrapper around a Runnable scheduled with a ScheduledExecutorService which is part of the java.util.concurrent package. The following is a gist of the core Runnable that handles the tick.

future = execService.scheduleWithFixedDelay(new Runnable() {
	@Override
	public void run() {
		onTick();
		elapsedTime += Timer.this.interval;
		if (duration > 0) {
			if(elapsedTime >=duration){
				onFinish();
				future.cancel(false);
			}
		}
	}
}, 0, interval, TimeUnit.MILLISECONDS);

For pause, we cancel the Future instance that we obtained when we scheduled the Runnable using the ExecutorService.

future.cancel(false);

For resume, we just schedule the same Runnable again.

We leave the following methods as abstract, to force the classes extending the Timer class to override them.

/**
*	This method is called periodically with the interval set as the delay between subsequent calls.
*/
	protected abstract void onTick();

/**
* This method is called once the timer has run for the specified duration. If the duration was set as infinity, then   * this method is never called.
*/
	protected abstract void onFinish();

Following is an example implementation of a concrete class extending the abstract Timer:

public class ExampleTimer extends Timer{

	public ExampleTimer() {
		super();
	}

	public ExampleTimer(long interval, long duration){
		super(interval, duration);
	}

	@Override
	protected void onTick() {
		System.out.println("onTick called!");
	}

	@Override
	protected void onFinish() {
		System.out.println("onFinish called!");
	}

}

Following is a simple test showing the usage of the Timer.

//This creates a timer which will tick every second indefinitely.
Timer oneSecondInfiniteTimer = new ExampleTimer();

//This creates a timer which ticks every 2 seconds, and runs for 20 seconds.
Timer twoSecondTimer = new ExampleTimer(2000l, 20000l);

//Start the timer.
twoSecondTimer.start();

//Pause the timer.
twoSecondTimer.pause();

//Resume the timer
twoSecondTimer.resume();

The whole code is hosted in GitHub (https://github.com/c05mic/pause-resume-timer ) for your copy-pasting pleasure. 🙂

Contributions are welcome!

Advertisements

Activity as a Dialog – Android

It’d be cool to have an Activity to be shown like a Dialog, with the previous activity still in the background.

It turns out that this can easily be done, making use of the android:theme attribute in your activity tag in the AndroidManifest.xml file.

<activity
 android:name=".YourActivityName"
 android:theme="@style/CustomDialog" >
 </activity>

where CustomDialog is a style that we declare in styles.xml (which should go into the res/values folder). Put the following inside the styles.xml.

<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="CustomDialog" parent="@android:style/Theme.Dialog">
 <item name="android:windowBackground">@color/transparent</item>
 <item name="android:windowIsFloating">true</item>
 <item name="android:windowNoTitle">true</item>
 </style>

</resources>

Start your activity, as you normally would, using startActivity(Intent intent).

Animating an image using Path and PathMeasure – Android

Animating an image along a predefined path (be it a line or a curve) is a common situation that game developers face. Instead of doing it the usual way (Finding the equation of the path, substituting x and finding y values and then using the co-ordinates [x,y] for the animation), we can do this in a more simpler way by making use of Path and PathMeasure.

If you already have a game loop running with its update and render cycles, the following code can easily be implemented. If you’re new to game loops, Obviam.net explains the concept behind game loops in a very concise manner.

Suppose, I want to move an image in a straight line from (0,0) to, say, (100, 100).

Following is a code snippet that shows you how to do it:

Declare the variables that we’d be using:

Path path;
PathMeasure measure;
float[] pos, tan;
float speed, distance;

Initialize your Path object and other variables that we’d be using later on. You could do this on your surfaceCreated() callback if you are using a SurfaceView.

// Init the Path.
 path=new Path();

// Set the starting position of the path to (0,0).
 path.moveTo(0,0); 

// Add a line to the Path, starting from (0,0), ending at (100, 100).
 path.lineTo(100,100); 

// Create a PathMeasure object, passing in the Path object
 // we created and a boolean that specifies if the Path should
 // be forced to a closed path.
 measure = new PathMeasure(path, false);

// Here, we're dividing the whole length of the path by 30.
 speed = measure.getLength() / 30;


pos=new float[2];
 tan=new float[2];

The concept behind the moving of the image is simple: Staring at (0,0), in every update cycle, we find a point along the defined path, traversing a little each time.
Now inside the update() method of our game loop, we’d be doing the following:

 public void update()
 {
 while(distance < measure.getLength())
 {

    // getPosTan pins the distance along the Path and
 // computes the position and the tangent.
 measure.getPosTan(distance, pos, tan);

     distance += speed;   // Traversal
 }
 }


Now to render,

 public void render(Canvas canvas)
 {

    //Draw the bitmap on the canvas, passing in the
 //Bitmap object, the x and y co-ordinate and a
 // Paint object.
 canvas.render(bmpImage, pos[0], pos[1], null);
 }
 

Here the speed directly determines the speed of the animation. Playing with that value, you can arrive at your desired speed.

Just like a line, you can also use curves, Bezier curves, arcs, etc., and a combination of these as well.

Hope this helps!