[EXPERIMENTAL] ⏰ Stopwatch - A Stopwatch Accurate To One Millisecond!

:computer: Introduction

A non-visible extension that acts like a stopwatch in your app and is accurate to 1/1000 of a second (one millisecond).

:warning: Doesn't work when screen on.

:package: Package name: com.gordonlu.stopwatch

:clock330: Version: 1

:date: Release date: 2022-03-30T07:30:00Z

:open_book: Documentation

Event blocks



This event is fired when the timer has gone off.

Parameters: minutes = number (int), seconds = number (int), milliseconds = number (int)

Method blocks



Checks whether the stopwatch is currently running.

Returns: boolean



Pauses the stopwatch so that after one millisecond, the event will not be fired anymore. You will have to resume the timer to continue firing.



Resumes the stopped stopwatch.



Starts the timer so that after one millisecond, the Timer event will be fired.



Stops the timer so that after one millisecond, the event will not be fired anymore. When this is called, the extension will fire one last Timer event for 0 minutes, 0 seconds and 0 milliseconds. To time again, you will have to start the timer again.

:bookmark_tabs: Why this extension?

The Clock component has been used for years by AI2 developers. Although it is useful, it is only accurate to at least 1/100th of a second. This extension is accurate to 1/1000th of a second, which is one millisecond.

:heavy_check_mark: Tests

Tested successfully on:

  • aiStarter emulator, Android 2.2.

  • Google Pixel 2, Android 6.

  • Google Pixel 2, Android 7.1.

  • Xiaom 5G NE, Android 11.

  • Google Pixel 5, Android 13.

:inbox_tray: Downloads

com.gordonlu.stopwatch.aix (8.4 KB)

AIA: StopWatch.aia (11.8 KB)

:warning: What's inside the AIA file?

  • 4 buttons for start, pause, resume and stop.

  • 1 clock accurate to 1/10th of a second to check the state of whether the stopwatch is running.

  • the stopwatch extension.

  • 1 label to show the stopwatch progress.

:lock: Open Source

Taken from here.

Here you go.

package com.gordonlu.stopwatch;

import android.app.Activity;
import android.content.Context;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;

import android.os.Handler;  
import android.os.SystemClock; 
import java.lang.Runnable;

        version = 1,
        description = "A non-visible extension that acts like a stopwatch in your app and is accurate to 1/1000 of a second (one millisecond).<br><br>Made by Gordon Lu (AICODE).",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,
        iconName = "https://docs.google.com/drawings/d/e/2PACX-1vQCI87PHLBF0jb8QWyYmIRQSjjNW3EFXf-qpsWCvBYkUQ9vEgPAB8SpxcMpblxNpbIYrjCjLrRLIU2c/pub?w=16&h=16")

@SimpleObject(external = true)
@UsesLibraries(libraries = "")
@UsesPermissions(permissionNames = "")

public class Stopwatch extends AndroidNonvisibleComponent {

    //Activity and Context
    private Context context;
    private Activity activity;
    long MillisecondTime, StartTime, TimeBuff, UpdateTime = 0L ;
    boolean paused = true;
    int Seconds, Minutes, MilliSeconds;  
    Handler handler = new Handler();    

    public Stopwatch(ComponentContainer container){
        this.activity = container.$context();
        this.context = container.$context();

    @SimpleFunction(description = "Starts the timer.")
    public void Start() {
        StartTime = SystemClock.uptimeMillis();  
        handler.postDelayed(runnable, 0);
        paused = false;

    @SimpleFunction(description = "Pauses the timer.")
    public void Pause() {
        TimeBuff += MillisecondTime;  
        paused = true;

    @SimpleFunction(description = "Resumes the paused timer.")
    public void Resume() {
        paused = false;

    @SimpleFunction(description = "Stops and resets the timer.")
    public void Stop() {
        MillisecondTime = 0L;  
        StartTime = 0L;  
        TimeBuff = 0L;  
        UpdateTime = 0L;  
        Seconds = 0;  
        Minutes = 0;  
        MilliSeconds = 0;
        Timer(0, 0, 0);
        paused = true;

    @SimpleEvent(description = "This event is fired when the timer has gone off.")
    public void Timer(int minutes, int seconds, int milliseconds) {
        EventDispatcher.dispatchEvent(this, "Timer", minutes, seconds, milliseconds);

    @SimpleFunction(description = "Checks whether the stopwatch is currently running.")
    public boolean IsRunning() {
        return !paused;

    public Runnable runnable = new Runnable() {  
        public void run() {  
            MillisecondTime = SystemClock.uptimeMillis() - StartTime;    
            UpdateTime = TimeBuff + MillisecondTime;    
            Seconds = (int) (UpdateTime / 1000);  
            Minutes = Seconds / 60;
            Seconds = Seconds % 60;
            MilliSeconds = (int) (UpdateTime % 1000);
            Timer(Minutes, Seconds, MilliSeconds);
            handler.postDelayed(runnable, 0);  

:+1: Rate my extension! :-1:

  • Good extension!
  • Bad extension.

0 voters

Made with Niotron IDE.

Kindly :email: PM me if you have any questions! Also, if you like my extension, please :heart: like it! It takes some effort for me to make it...

Votes and likes tell me the general user feedback of my extension. If you read this extension, please take 20 seconds to drop by and give a vote / like!

If you have any features that you want to add and you know the code, PM me or directly reply below using the image button.

Gordon Lu

:speech_balloon: Message :earth_africa: Website :e-mail: E-mail


Test on a Galaxy Note8 (Android 9) with Companion in idle mode (screen off):

After the app is brought back from idle mode, the time is not caught up again.

So the app loses more than 34 seconds after just 1 minute.

1 Like

This doesn't work when you turn the screen off. It only works when the screen is on.

Yes, I just wanted to point that out.

Here is an app that also works in the background & in idle mode and doesn't lose a single millisecond, even if it runs for hours or days.


It's not possible to stop the Stopwatch in the Stopwatch.Timer event:



1 Like

That's a bug in the current version. I'll fix it in the next version.

The same applies to pause.

Here are my blocks (of my extension):


Hmm...I can't find a solution for how to stop the bug. I'll try in a different approach. I have added the experimental tag to the extension (because it is not working properly).