StopWatch Sample Overview
The StopWatch sample application demonstrates how you can operate a stopwatch and timer.
For information on creating the sample application project in the IDE, see Creating Sample Applications.
The following figure illustrates the main screen of the StopWatch.
Figure: StopWatch screen
The application opens on the StopWatch tab. To switch to the timer tab, click Timer.
You can use the stopwatch to record how much time something takes with a millisecond precision. You can also track intermediate times (laps), for example, in sporting events and races. On the StopWatch tab:
- To start the stopwatch, click START.
The timer starts running at the top of the screen.
- To record a lap time, click LAP.
The timer continues running.
- To stop the stopwatch, click STOP.
The timer stops running.
- To reset the stopwatch, click RESET.
The timer is set to 00:00:00.
- To restart the stopwatch, click RESTART.
The timer continues running from the last recorded time.
You can use a timer to count down a specific time period when an alarm is triggered. The alarm is triggered even if the application is closed. On the Timer tab:
- To start the timer, click the numbers to set the time period, and click START.
- To stop the timer, click STOP.
- To reset the timer, click RESET.
The timer is set to 00:01:00.
Source Files
You can create and view the sample application project including the source files in the IDE.
File name | Description |
---|---|
config.xml | This file contains the application information for the platform to install and launch the application, including the view mode and the icon to be used in the device menu. |
css/style.css | This file contains the CSS styling for the application UI. |
img/ | This directory contains the images used to create the user interface. |
index.html | This is a starting file from which the application starts loading. It contains the layout of the application screens. |
js/app.js | This file contains the application initializer. |
js/core/ | This directory contains the application framework. |
js/helpers/page.js | This file contains the helpers for retrieving information about an opened page. |
js/helpers/timer.js | This file contains the definition of the helper class Timer transforming the milliseconds to the hh:MM:ss:ms format. |
js/models/ | This directory contains the code related to the application model. |
js/views/ | This directory contains the code related to application UI. |
lib/tau/ | This directory contains the external libraries (TAU library). |
Implementation
This section describes the implementation of the core functionality of the application. The code related to the user interface and functions not related to the Tizen APIs have been omitted to preserve simplicity.
Stopwatch Implementation
To implement the stopwatch functionality, define the Timer class.
/* js/models/timer.js */ function Timer(delay, callbacks) { this.reset(); this.callbacks = callbacks; this.delay = delay; this.id = setInterval(this.tick.bind(this), this.delay); } Timer.prototype = { pause: function pause() { (...) }, reset: function reset() { (...) }, run: function run() { (...) }, stop: function stop() { (...) }, getTimeElapsed: function getTimeElapsed() { (...) }, lap: function lap() { (...) }, tick: function tick() { (...) } };
The above class consists of the following methods:
- reset() – resets the stopwatch and clears the laps.
- run() – starts the stopwatch.
- lap() – adds a lap.
- pause() – pauses the stopwatch. It can be resumed later.
- stop() – stops the stopwatch permanently. It cannot be resumed anymore.
- getElapsedTime() – returns the elapsed time.
- tick() – a callback that is executed repeatedly when the stopwatch is not paused or stopped.
The constructor of the class gets a delay value and a callbacks array on input.
First the constructor resets the timer instance by executing the reset() method, and then it starts executing the tick() method repeatedly after every delay.
The reset() method initializes the internal variables.
/* js/models/timer.js */ reset: function reset() { this.status = 'ready'; this.startTime = null; this.lapNo = 1; this.lastLapTime = 0; }
The status variable indicates the status of the stopwatch. It is set as 'ready' to show that the stopwatch is initialized but not started yet. The startTime variable contains a timestamp where the stopwatch was started. The lapNo and lastLapTime are used in creating laps.
The tick() method executes the callbacks if the timer is running.
/* js/models/timer.js */ tick: function tick() { var i; if (this.status !== 'running') { return this; } (...) /* Execute callbacks */ return this; }
To run a timer, call the run() method.
/* js/models/timer.js */ run: function run() { switch (this.status) { case 'ready': if (this.startTime === null) { this.startTime = Date.now(); } break; case 'paused': /* Adjust the startTime by the time passed since the pause */ /* so that the time elapsed remains unchanged */ this.startTime += Date.now() - this.timePaused; break; case 'running': /* Already running */ return this; case 'stopped': throw new Error('Can\'t run a stopped timer again'); } this.status = 'running'; return this; }
The run() method checks the status value. If the stopwatch is in the 'ready' state, the startTime variable is set as the current timestamp. If the status variable has the 'paused' value, the startTime variable is adjusted and the time elapsed remains unchanged. If the stopwatch is stopped, the function throws an exception as the stopping operation is not possible.
After checking the above, the status variable is set as the 'running' value.
When the stopwatch is running, it can be paused by calling the pause() method.
/* js/models/timer.js */ pause: function pause() { if (this.status !== 'running') { throw new Error('Can pause only a running timer'); } this.status = 'paused'; this.timePaused = Date.now(); return this; }
When the timer instance is no longer needed, it can be stopped permanently by calling the stop method.
/* js/models/timer.js */ stop: function stop() { clearInterval(this.id); this.status = 'stopped'; this.timePaused = null; return this; }
To record laps, use the Lap() method.
/* js/models/timer.js */ function Lap(no, time) { this.no = no; this.time = time; }
An instance of the Lap class has two variables: no indicating the lap number and time indicating how much time passed from the last lap to now.
To implement a function registering a lap, the getTimeElapsed() function returns information on how much time has passed.
/* js/models/timer.js */ getTimeElapsed: function getTimeElapsed() { if (this.status === 'running') { return Date.now() - this.startTime; } if (this.status === 'paused') { return this.timePaused - this.startTime; } return 0; }
The following function registers a lap.
/* js/models/timer.js */ lap: function lap() { var lapObj = new Lap( this.lapNo, /* Lap time is total time minus previous lap time */ this.getTimeElapsed() - this.lastLapTime); this.lastLapTime = this.getTimeElapsed(); this.lapNo += 1; return lapObj; }
Timer Implementation
The Timer is based on the Timer class implementation.
When the user sets the time and taps the START button, the clock on top of the page starts counting down. When the clock reaches zero, the alarm rings and the alarm page is displayed.
The following function initializes the timer object and sets the alarm file path to the alarm module.
/* js/views/timerPage.js */ var ALARM_SOUND_PATH = 'sounds/sounds_alarm.mp3'; function init() { timer = new Timer(100, fireTick); audio.setFile(ALARM_SOUND_PATH); }
The audio.setFile(ALARM_SOUND_PATH) command sets the audio path to the audio module of the application framework.
/* js/core/core/audio.js */ var audio = new window.Audio(); function setFile(path) { audio.src = path; audio.autoplay = false; audio.loop = false; audio.load(); }
The timer is initialized with the fireTick callback. This callback updates the UI and calls the following function.
/* js/views/timerPage.js */ function tick() { var timeDiff = startTimeMilli - timer.getTimeElapsed(), time = parseInt(sTimerKeyBase, 10); if (timeDiff < 0) { timeUp(time); timer.reset(); } }
The tick() function checks whether the time set by the user has already passed. If so, the timer is reset and the timeUp() function is called. The function uses the sTimerKeyBase variable containing the time (in seconds) chosen by the user.
The timeUp function opens the alarm page by calling the showAlarm method and plays the alarm sound.
/* js/views/timerPage.js */ function timeUp() { showAlarm(); audio.play({loop: true, file: ALARM_SOUND_PATH}); }
The showAlarm function opens the alarm page and turns on the screen.
/* js/views/timerPage.js */ function showAlarm() { (...) /* Open the alarm page */ tizen.power.turnScreenOn(); }
The audio.play({loop: true, file: ALARM_SOUND_PATH}) command executes the play() function from the application framework to repeatedly play the specified audio file.
/* js/core/core/audio.js */ function play(options) { options = options || {}; if (options.file !== undefined) { setFile(options.file); } if (options.loop !== undefined) { loop(options.loop); } audio.play(); return true; } function loop(flag) { audio.loop = flag; }
To start the timer, the user must tap the START button. It calls the startCountdown() function.
/* js/views/timerPage.js */ function startCountdown() { timer.run(); power.awake(); }
The startCountdown() function starts the timer and awakes screen by calling the power.awake method on the Core application framework power module.
/* js/core/core/power.js */ var power = tizen.power, RESOURCE = 'SCREEN', STATE_NORMAL = 'SCREEN_NORMAL'; function awake() { power.request(RESOURCE, STATE_NORMAL); }
To run the alarm when the application is closed, use the Alarm API. Handle the visibilitychange event to set a system alarm when the application is closed.
/* js/app.js */ document.addEventListener('visibilitychange', onVisibilityChange);
The onVisibilityChange function invokes the following visibilityChange function.
/* js/views/timerPage.js */ function visibilityChange(ev) { var state = document.visibilityState; if (state !== 'visible') { if (timer.status === 'running') { addAlarm((startTimeMilli - timer.getTimeElapsed()) / 1000); } } }
If the timer is running, add the system alarm by calling the addAlarm function.
/* js/views/timerPage.js */ function addAlarm(time) { var alarm = new tizen.AlarmRelative(time + 1), appControl = new tizen.ApplicationControl('http://tizen.org/appcontrol/operation/alarm/' + sTimerKeyBase); tizen.alarm.add(alarm, tizen.application.getCurrentApplication().appInfo.id, appControl); }
The alarm is added by calling the tizen.alarm.add function. This function gets a tizen.AlarmRelative object, an application ID string, and a tizen.ApplicationControl object. The first parameter contains information on when the alarm must be invoked. The second parameter is the ID of the application. When the time passes, the system opens an application with this ID. The third parameter contains the operation name concatenated with the time set by user. When the application is opened by the system, this information can be used by it.
When the application is opened, the init() function is called. This is the right place to check whether the application is opened by the Alarm API.
/* js/core/core/application.js */ app = tizen.application; /* js/app.js */ var OPERATION_ALARM = 'http://tizen.org/appcontrol/operation/alarm'; function init() { var appOperation = app.getCurrentApplication() .getRequestedAppControl() .appControl .operation; if (appOperation.indexOf(OPERATION_ALARM) !== -1) { (...) /* Open the alarm page */ } }
If the application was opened by the Alarm API then the init function above executes the following function:
/* js/views/timerPage.js */ function timeAlarmFire(event) { var event_time = parseInt(event.detail.time, 10), time = parseInt(sTimerKeyBase, 10); if (time !== event_time) { time = event_time; } timeUp(time); }
This function calls the timeUp() function, the alarm page is displayed, and alarm starts ringing.