├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── stacktex
│ │ └── mobin
│ │ └── search
│ │ └── countdowntimer
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── stacktex
│ │ │ └── mobin
│ │ │ └── search
│ │ │ └── countdowntimer
│ │ │ ├── SimpleCountDownTimer.java
│ │ │ ├── SimpleCountDownTimerKotlin.kt
│ │ │ └── activity
│ │ │ ├── Main2Activity.java
│ │ │ └── MainActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── stacktex
│ └── mobin
│ └── search
│ └── countdowntimer
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | # Android CountDown Timer
2 |
3 | :alarm_clock:
4 |
5 | A simple countdown timer identical to [android.os.CountDownTimer](https://developer.android.com/reference/android/os/CountDownTimer) but with simplified usage and additional functionality to pause,resume and run in background.
6 |
7 |
8 | ## Features
9 | - Easy to use.
10 | - Dynamic delays.
11 | - Allows pause/resume/stop/restart.
12 | - Allows instant switch & run on background thread.
13 | - Allows timer pattern formatting.
14 | - Versions available for both Java & Kotlin.
15 | - Android only (Doesn't rely to work on any 3rd party library).
16 | - Its not a fixed dependency to be included in your project to increase redundancy.
17 | - Its flexible to be converted in any library/SDK or modular form as per your requirement.
18 | - Modifications/Enhancements can be made as required.
19 | - Highly decoupled,optimized and clean code.
20 | - No Obfuscation Required (Proguard/Dexguard).
21 | - Complete Documentation.
22 | - **It would be a part of your project while not implying any 3rd-party involvement.**
23 |
24 | ### How to use ?
25 |
26 | Just clone the project in Android Studio and run it.
27 |
28 | For details read more on [medium](https://android.jlelse.eu/countdown-timer-in-android-941aab8dc976).
29 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion 29
9 | defaultConfig {
10 | applicationId "stacktex.mobin.search.countdowntimer"
11 | minSdkVersion 21
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility = 1.8
25 | targetCompatibility = 1.8
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
32 | implementation 'androidx.appcompat:appcompat:1.1.0'
33 | implementation 'androidx.core:core-ktx:1.1.0'
34 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
35 | testImplementation 'junit:junit:4.12'
36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
38 | }
39 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/stacktex/mobin/search/countdowntimer/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("stacktex.mobin.search.countdowntimer", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/stacktex/mobin/search/countdowntimer/SimpleCountDownTimer.java:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer;
2 |
3 | import android.os.Handler;
4 | import android.os.HandlerThread;
5 |
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.text.SimpleDateFormat;
9 | import java.util.Calendar;
10 | import java.util.Locale;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | /**
14 | * A simple countdown timer identical to android.os.CountDownTimer but with simplified usage and additional functionality to pause and resume.
15 | * Note: This timer runs on UI thread by default but that can be changed by calling runOnBackgroundThread at any time.
16 | * @author Mobin Munir
17 | */
18 | public final class SimpleCountDownTimer {
19 | private OnCountDownListener onCountDownListener;
20 | private long fromMinutes;
21 | private long fromSeconds;
22 | private long delayInSeconds = 1;
23 | private Calendar calendar = Calendar.getInstance();
24 | private long seconds, minutes;
25 | private boolean finished;
26 | private Handler handler = new Handler();
27 | private HandlerThread handlerThread;
28 | private boolean isBackgroundThreadRunning;
29 | private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss", Locale.getDefault());
30 | private Runnable runnable = this::decrementMinutes;
31 |
32 | /**
33 | * @param fromMinutes minutes to countdown.
34 | * @param fromSeconds seconds to countdown.
35 | * @param onCountDownListener A listener for countdown ticks.
36 | * @param delayInSeconds optional delay in seconds for a tick to execute default is 1 second.
37 | */
38 | public SimpleCountDownTimer(long fromMinutes, long fromSeconds, long delayInSeconds, OnCountDownListener onCountDownListener) {
39 |
40 | if (fromMinutes <= 0 && fromSeconds <= 0)
41 | throw new IllegalStateException(getClass().getSimpleName() + " can't work in state 0:00");
42 |
43 | if (delayInSeconds > 1)
44 | this.delayInSeconds = delayInSeconds;
45 |
46 | this.onCountDownListener = onCountDownListener;
47 |
48 |
49 | setCountDownValues(fromMinutes, fromSeconds);
50 |
51 |
52 | }
53 |
54 | /**
55 | * This method sets business logic for countdown operation before it starts.
56 | */
57 | private void setCountDownValues(long fromMinutes, long fromSeconds) {
58 | this.fromMinutes = fromMinutes;
59 | this.fromSeconds = fromSeconds;
60 | minutes = this.fromMinutes;
61 |
62 | if (fromMinutes > 0 && fromSeconds <= 0) {
63 | seconds = 0;
64 | return;
65 | }
66 |
67 | if (fromSeconds <= 0 || fromSeconds > 59) {
68 | seconds = 59;
69 | return;
70 | }
71 |
72 | seconds = this.fromSeconds;
73 |
74 | }
75 |
76 |
77 | /**
78 | * @return This method returns seconds till countdown.
79 | */
80 | public long getSecondsTillCountDown() {
81 | return seconds;
82 | }
83 |
84 | /**
85 | * @return This method returns minutes till countdown.
86 | */
87 | public long getMinutesTillCountDown() {
88 | return minutes;
89 | }
90 |
91 | /**
92 | * Sets a new pattern for SimpleDateFormat for time returned on each tick.
93 | *
94 | * @param pattern only acceptable "mm:ss","m:s","mm","ss","m","s".
95 | */
96 |
97 | public void setTimerPattern(String pattern) {
98 | if (pattern.equalsIgnoreCase("mm:ss") || pattern.equalsIgnoreCase("m:s") || pattern.equalsIgnoreCase("mm") ||
99 | pattern.equalsIgnoreCase("ss") || pattern.equalsIgnoreCase("m") || pattern.equalsIgnoreCase("s"))
100 | simpleDateFormat.applyPattern(pattern);
101 | }
102 |
103 | /**
104 | * This method call will permanently move the timer to run in background thread for this instance.
105 | * A new thread is created which is then bound to timer's handler of main thread's message queue therefore overwriting it.
106 | * This method can be invoked at any time.
107 | * Note: onCountDownListener callbacks will not be invoked on main thread.
108 | */
109 | public final void runOnBackgroundThread() {
110 |
111 | if (isBackgroundThreadRunning)
112 | return;
113 |
114 | handlerThread = new HandlerThread(getClass().getSimpleName());
115 |
116 | startBackgroundThreadIfNotRunningAndEnabled();
117 |
118 | handler = new Handler(handlerThread.getLooper());
119 |
120 |
121 | }
122 |
123 | private void startBackgroundThreadIfNotRunningAndEnabled() {
124 | if (handlerThread != null && !handlerThread.isAlive()) {
125 | handlerThread.start();
126 | isBackgroundThreadRunning = true;
127 | }
128 | }
129 |
130 | /**
131 | * No need to quit background thread once started.
132 | * Quitting it kills it. Threads don't restart.
133 | * This is just left here if needed for any reason in future.
134 | */
135 | /* private void quitBackgroundThreadSafelyIfRunning() {
136 |
137 | if (!isBackgroundThreadRunning)
138 | return;
139 |
140 | isBackgroundThreadRunning = !handlerThread.quitSafely();
141 | }*/
142 | @NotNull
143 | private String getCountDownTime() {
144 | calendar.set(Calendar.MINUTE, (int) minutes);
145 | calendar.set(Calendar.SECOND, (int) seconds);
146 | return simpleDateFormat.format(calendar.getTime());
147 | }
148 |
149 | private void decrementMinutes() {
150 |
151 | seconds--;
152 |
153 | if (minutes == 0 && seconds == 0) {
154 | finish();
155 | }
156 |
157 | if (seconds < 0) {
158 | if (minutes > 0) {
159 | seconds = 59;
160 | minutes--;
161 | }
162 |
163 | }
164 |
165 |
166 | runCountdown();
167 |
168 |
169 | }
170 |
171 | private void finish() {
172 | onCountDownListener.onCountDownFinished();
173 | finished = true;
174 | pause();
175 | }
176 |
177 | private void decrementSeconds() {
178 | handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(delayInSeconds));
179 |
180 | }
181 |
182 | /**
183 | * A method to start/resume countdown.
184 | *
185 | * @param resume if true it will resume from where its paused else from start.
186 | */
187 | public final void start(boolean resume) {
188 |
189 | if (!resume) {
190 | setCountDownValues(fromMinutes, fromSeconds);
191 | finished = false;
192 | }
193 |
194 | runCountdown();
195 |
196 |
197 | }
198 |
199 | private void runCountdown() {
200 | if (!finished) {
201 | updateUI();
202 | decrementSeconds();
203 | }
204 | }
205 |
206 | private void updateUI() {
207 | onCountDownListener.onCountDownActive(getCountDownTime());
208 | }
209 |
210 | /**
211 | * A method to pause/stop countdown.
212 | */
213 | public final void pause() {
214 | handler.removeCallbacks(runnable);
215 | }
216 |
217 | /**
218 | * A countdown listener to be used to listen for ticks and finish.
219 | */
220 | public interface OnCountDownListener {
221 | /**
222 | * A method continuously called on ticking.
223 | *
224 | * @param time The time at tick.
225 | */
226 | void onCountDownActive(String time);
227 |
228 | /**
229 | * A method called once when countdown is finished.
230 | */
231 | void onCountDownFinished();
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/app/src/main/java/stacktex/mobin/search/countdowntimer/SimpleCountDownTimerKotlin.kt:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer
2 |
3 | import android.os.Handler
4 | import android.os.HandlerThread
5 | import java.text.SimpleDateFormat
6 | import java.util.*
7 | import java.util.concurrent.TimeUnit
8 |
9 | /**
10 | * A simple countdown timer identical to android.os.CountDownTimer but with simplified usage and additional functionality to pause and resume.
11 | * @param fromMinutes minutes to countdown.
12 | * @param fromSeconds seconds to countdown.
13 | * @param onCountDownListener A listener for countdown ticks.
14 | * @param delayInSeconds optional delay in seconds for a tick to execute default is 1 second.
15 | * Note: This timer runs on UI thread by default but that can be changed by calling runOnBackgroundThread at any time.
16 | * @author Mobin Munir
17 | */
18 | class SimpleCountDownTimerKotlin(
19 | private var fromMinutes: Long,
20 | private var fromSeconds: Long,
21 | private val onCountDownListener: OnCountDownListener,
22 | private var delayInSeconds: Long = 1
23 | ) {
24 |
25 | private val calendar = Calendar.getInstance()
26 | private var seconds = 0L
27 | private var minutes = 0L
28 | private var finished = false
29 | private var handler = Handler()
30 | private var handlerThread: HandlerThread? = null
31 | private var isBackgroundThreadRunning = false
32 | private val simpleDateFormat = SimpleDateFormat("mm:ss", Locale.getDefault())
33 | private val runnable = Runnable { decrementMinutes() }
34 |
35 | init {
36 | check(!(fromMinutes <= 0 && fromSeconds <= 0)) { javaClass.simpleName + " can't work in state 0:00" }
37 |
38 | if (delayInSeconds <= 0)
39 | delayInSeconds = 1
40 |
41 | setCountDownValues()
42 | }
43 |
44 | /**
45 | * This method sets business logic for countdown operation before it starts.
46 | */
47 | private fun setCountDownValues(
48 | fromMinutes: Long = this.fromMinutes,
49 | fromSeconds: Long = this.fromSeconds
50 | ) {
51 | this.fromMinutes = fromMinutes
52 | this.fromSeconds = fromSeconds
53 | minutes = this.fromMinutes
54 |
55 | if (fromMinutes > 0 && fromSeconds <= 0) {
56 | seconds = 0
57 | return
58 | }
59 |
60 | if (fromSeconds <= 0 || fromSeconds > 59) {
61 | seconds = 59
62 | return
63 | }
64 | seconds = this.fromSeconds
65 | }
66 |
67 | /**
68 | * @return This method returns seconds till countdown.
69 | */
70 | fun getSecondsTillCountDown() = seconds
71 |
72 |
73 | /**
74 | * @return This method returns minutes till countdown.
75 | */
76 | fun getMinutesTillCountDown() = minutes
77 |
78 |
79 | /**
80 | * Sets a new pattern for SimpleDateFormat for time returned on each tick.
81 | * @param pattern only acceptable "mm:ss","m:s","mm","ss","m","s".
82 | */
83 | fun setTimerPattern(pattern: String) {
84 | if (pattern.equals("mm:ss", ignoreCase = true) || pattern.equals(
85 | "m:s",
86 | ignoreCase = true
87 | ) || pattern.equals("mm", ignoreCase = true) ||
88 | pattern.equals("ss", ignoreCase = true) || pattern.equals(
89 | "m",
90 | ignoreCase = true
91 | ) || pattern.equals("s", ignoreCase = true)
92 | ) simpleDateFormat.applyPattern(pattern)
93 | }
94 |
95 | /**
96 | * This method call will permanently move the timer to run in background thread for this instance.
97 | * A new thread is created which is then bound to timer's handler of main thread's message queue therefore overwriting it.
98 | * This method can be invoked at any time.
99 | * Note: onCountDownListener callbacks will not be invoked on main thread.
100 | */
101 | fun runOnBackgroundThread() {
102 | if (isBackgroundThreadRunning) return
103 | handlerThread = HandlerThread(javaClass.simpleName)
104 | startBackgroundThreadIfNotRunningAndEnabled()
105 | handler = Handler(handlerThread!!.looper)
106 | }
107 |
108 | private fun startBackgroundThreadIfNotRunningAndEnabled() {
109 |
110 | handlerThread!!.run {
111 | start()
112 | isBackgroundThreadRunning = true
113 | }
114 |
115 | }
116 |
117 | private fun getCountDownTime(): String {
118 | calendar[Calendar.MINUTE] = minutes.toInt()
119 | calendar[Calendar.SECOND] = seconds.toInt()
120 | return simpleDateFormat.format(calendar.time)
121 | }
122 |
123 | private fun decrementMinutes() {
124 | seconds--
125 |
126 | if (minutes == 0L && seconds == 0L) {
127 | finish()
128 | }
129 |
130 | if (seconds < 0L) {
131 | if (minutes > 0) {
132 | seconds = 59
133 | minutes--
134 | }
135 | }
136 |
137 |
138 | runCountdown()
139 | }
140 |
141 | private fun finish() {
142 | onCountDownListener.onCountDownFinished()
143 | finished = true
144 | pause()
145 | }
146 |
147 | private fun decrementSeconds() {
148 | handler.postDelayed(
149 | runnable,
150 | TimeUnit.SECONDS.toMillis(delayInSeconds)
151 | )
152 | }
153 |
154 | /**
155 | * A method to start/resume countdown.
156 | *
157 | * @param resume if true it will resume from where its paused else from start.
158 | */
159 | fun start(resume: Boolean = false) {
160 | if (!resume) {
161 | setCountDownValues()
162 | finished = false
163 | }
164 | runCountdown()
165 | }
166 |
167 | private fun runCountdown() {
168 | if (!finished) {
169 | updateUI()
170 | decrementSeconds()
171 | }
172 | }
173 |
174 | private fun updateUI() {
175 | onCountDownListener.onCountDownActive(getCountDownTime())
176 | }
177 |
178 | /**
179 | * A method to pause/stop countdown.
180 | */
181 | fun pause() {
182 | handler.removeCallbacks(runnable)
183 | }
184 |
185 | /**
186 | * A countdown listener to be used to listen for ticks and finish.
187 | */
188 | interface OnCountDownListener {
189 | /**A method continuously called on ticking.
190 | * @param time The time at tick.
191 | */
192 | fun onCountDownActive(time: String)
193 |
194 | /**
195 | * A method called once when countdown is finished.
196 | */
197 | fun onCountDownFinished()
198 | }
199 | }
--------------------------------------------------------------------------------
/app/src/main/java/stacktex/mobin/search/countdowntimer/activity/Main2Activity.java:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer.activity;
2 |
3 | import android.os.Bundle;
4 | import android.widget.Button;
5 | import android.widget.TextView;
6 | import android.widget.Toast;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 |
10 | import stacktex.mobin.search.countdowntimer.R;
11 | import stacktex.mobin.search.countdowntimer.SimpleCountDownTimer;
12 |
13 | public class Main2Activity extends AppCompatActivity implements SimpleCountDownTimer.OnCountDownListener {
14 | private TextView textView;
15 | private Button start;
16 | private Button resume;
17 |
18 | private final SimpleCountDownTimer simpleCountDownTimer = new SimpleCountDownTimer(0, 10, 1, this);
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_main);
24 | textView = findViewById(R.id.tv);
25 | start = findViewById(R.id.startBtn);
26 | resume = findViewById(R.id.resumeBtn);
27 | Button pause = findViewById(R.id.pauseBtn);
28 |
29 | resume.setEnabled(false);
30 |
31 | start.setOnClickListener(view -> {
32 | simpleCountDownTimer.start(false);
33 |
34 | start.setEnabled(false);
35 |
36 | });
37 |
38 | resume.setOnClickListener(view -> {
39 | simpleCountDownTimer.start(true);
40 | simpleCountDownTimer.runOnBackgroundThread();
41 | });
42 | pause.setOnClickListener(view -> {
43 | simpleCountDownTimer.pause();
44 | simpleCountDownTimer.setTimerPattern("s");
45 | resume.setEnabled(true);
46 | });
47 |
48 |
49 | }
50 |
51 |
52 | @Override
53 | public void onCountDownActive(String time) {
54 |
55 | textView.post(() -> textView.setText(time));
56 |
57 | Toast.makeText(this, "Seconds = " + simpleCountDownTimer.getSecondsTillCountDown() + " Minutes=" + simpleCountDownTimer.getMinutesTillCountDown(), Toast.LENGTH_SHORT).show();
58 |
59 | }
60 |
61 | @Override
62 | public void onCountDownFinished() {
63 | textView.post(() -> {
64 | textView.setText("Finished");
65 | start.setEnabled(true);
66 | resume.setEnabled(false);
67 | });
68 |
69 |
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/stacktex/mobin/search/countdowntimer/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer.activity
2 |
3 | import android.os.Bundle
4 | import android.widget.Toast
5 | import androidx.appcompat.app.AppCompatActivity
6 | import kotlinx.android.synthetic.main.activity_main.*
7 | import stacktex.mobin.search.countdowntimer.R
8 | import stacktex.mobin.search.countdowntimer.SimpleCountDownTimerKotlin
9 |
10 | class MainActivity : AppCompatActivity(),
11 | SimpleCountDownTimerKotlin.OnCountDownListener {
12 |
13 | private val countDownTimer =
14 | SimpleCountDownTimerKotlin(
15 | 0,
16 | 10,
17 | this
18 | )
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_main)
23 |
24 | resumeBtn.isEnabled = false
25 |
26 | startBtn.setOnClickListener {
27 | countDownTimer.start()
28 |
29 | startBtn.isEnabled = false
30 | }
31 |
32 | resumeBtn.setOnClickListener {
33 | countDownTimer.start(true)
34 | countDownTimer.runOnBackgroundThread()
35 | }
36 |
37 | pauseBtn.setOnClickListener {
38 | countDownTimer.pause()
39 | resumeBtn.isEnabled = true
40 | countDownTimer.setTimerPattern("s")
41 | }
42 | }
43 |
44 | override fun onCountDownActive(time: String) {
45 |
46 | runOnUiThread {
47 |
48 | tv.text = time
49 |
50 | Toast.makeText(
51 | this,
52 | "Seconds = " + countDownTimer.getSecondsTillCountDown() + " Minutes=" + countDownTimer.getMinutesTillCountDown(),
53 | Toast.LENGTH_SHORT
54 | ).show()
55 | }
56 |
57 | }
58 |
59 | override fun onCountDownFinished() {
60 | runOnUiThread {
61 | tv.text = "Finished"
62 | startBtn.isEnabled = true
63 | resumeBtn.isEnabled = false
64 | }
65 |
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
27 |
28 |
35 |
36 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmobin789/Android-CountDownTimer/3e0d959fc5dc5a54f91995ff7d0986554bbc3b99/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CountDownTimer
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/stacktex/mobin/search/countdowntimer/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package stacktex.mobin.search.countdowntimer
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.61'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.5.3'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 |
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 20 20:07:05 PKT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='CountDownTimer'
3 |
--------------------------------------------------------------------------------