-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
basic timer notification with stop button
- Loading branch information
Showing
7 changed files
with
233 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 67 additions & 1 deletion
68
android/app/src/main/kotlin/dev/qori/interval/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,72 @@ | ||
package dev.qori.interval | ||
|
||
import android.app.NotificationChannel | ||
import android.app.NotificationManager | ||
import android.content.Intent | ||
import android.os.Build | ||
import io.flutter.embedding.android.FlutterActivity | ||
import io.flutter.embedding.engine.FlutterEngine | ||
import io.flutter.plugin.common.MethodCall | ||
import io.flutter.plugin.common.MethodChannel | ||
|
||
class MainActivity: FlutterActivity() { | ||
class MainActivity : FlutterActivity() { | ||
|
||
companion object { | ||
const val FLUTTER_CHANNEL_ID = "interval.qori.dev/notification" | ||
} | ||
|
||
private var onTimerDismissed:((Unit)->Unit)? = null | ||
|
||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { | ||
super.configureFlutterEngine(flutterEngine) | ||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
// Create the NotificationChannel. | ||
val name = "timer notification" | ||
val descriptionText = "to show the remaining time when doing a task" | ||
val importance = NotificationManager.IMPORTANCE_DEFAULT | ||
val mChannel = NotificationChannel(TimerService.NOTIFICATION_CHANNEL_ID, name, importance) | ||
mChannel.description = descriptionText | ||
// Register the channel with the system. You can't change the importance | ||
// or other notification behaviors after this. | ||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager | ||
notificationManager.createNotificationChannel(mChannel) | ||
} | ||
|
||
|
||
val channel = MethodChannel(flutterEngine .dartExecutor.binaryMessenger, FLUTTER_CHANNEL_ID) | ||
|
||
channel.setMethodCallHandler { call, result -> | ||
when (call.method) { | ||
"showTimer" -> handleShowTimer(call) | ||
"dismissTimer" -> handleDismissTimer(call) | ||
else -> result.notImplemented() | ||
} | ||
} | ||
|
||
val _onTimerDismissed = {_:Unit-> | ||
channel.invokeMethod("onTimerDismissed", null) | ||
} | ||
TimerService.timerDismissedObservable.addSubscriber(_onTimerDismissed) | ||
onTimerDismissed = _onTimerDismissed | ||
} | ||
|
||
override fun onDestroy() { | ||
super.onDestroy() | ||
val cb = onTimerDismissed | ||
if (cb!==null){ | ||
TimerService.timerDismissedObservable.removeSubscriber(cb) | ||
} | ||
} | ||
|
||
private fun handleShowTimer(call: MethodCall) { | ||
val taskName = call.argument<String>("taskName")!! | ||
val formattedDuration = call.argument<String>("formattedDuration")!! | ||
|
||
TimerService.showTimer(this, taskName, formattedDuration) | ||
} | ||
|
||
private fun handleDismissTimer(call: MethodCall) { | ||
TimerService.dismissTimer(this) | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
android/app/src/main/kotlin/dev/qori/interval/Observable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package dev.qori.interval | ||
|
||
class Observable<T> { | ||
private val subscribers = mutableListOf<(T)->Unit>() | ||
|
||
fun addSubscriber(subscriber:(T)->Unit){ | ||
subscribers.add(subscriber) | ||
} | ||
|
||
fun removeSubscriber(subscriber:(T)->Unit){ | ||
subscribers.remove(subscriber) | ||
} | ||
|
||
fun notifySubscribers(data:T){ | ||
subscribers.forEach { sub-> | ||
sub(data) | ||
} | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
android/app/src/main/kotlin/dev/qori/interval/TimerService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package dev.qori.interval | ||
|
||
import android.app.Notification | ||
import android.app.PendingIntent | ||
import android.app.Service | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Build | ||
import android.os.IBinder | ||
import androidx.core.app.NotificationCompat | ||
|
||
class TimerService : Service() { | ||
companion object { | ||
const val NOTIFICATION_CHANNEL_ID = "TimerServiceNotificationChannelID" | ||
const val ACTION_STOP = "ACTION_STOP" | ||
const val ACTION_SHOW = "ACTION_SHOW" | ||
val timerDismissedObservable = Observable<Unit>() | ||
|
||
fun showTimer(ctx: Context, taskName: String, formattedTime: String) { | ||
val i = Intent(ctx, TimerService::class.java) | ||
i.action = ACTION_SHOW | ||
i.putExtra("taskName", taskName) | ||
i.putExtra("formattedTime", formattedTime) | ||
start(ctx, i) | ||
} | ||
|
||
fun dismissTimer(ctx: Context) { | ||
val i = Intent(ctx, TimerService::class.java) | ||
i.action = ACTION_STOP | ||
start(ctx, i) | ||
} | ||
|
||
private fun start(ctx: Context, i: Intent) { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
ctx.startForegroundService(i) | ||
} | ||
ctx.startService(i) | ||
} | ||
} | ||
|
||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | ||
if (intent !== null) { | ||
when (intent.action) { | ||
ACTION_SHOW -> { | ||
val taskName = intent.getStringExtra("taskName")!! | ||
val formattedTime = intent.getStringExtra("formattedTime")!! | ||
startForeground(1, makeNotif(taskName, formattedTime)) | ||
} | ||
|
||
ACTION_STOP -> { | ||
timerDismissedObservable.notifySubscribers(Unit) | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
stopForeground(STOP_FOREGROUND_REMOVE) | ||
} else { | ||
@Suppress("DEPRECATION") | ||
stopForeground(true) | ||
} | ||
} | ||
|
||
else -> Unit | ||
|
||
} | ||
} | ||
return START_NOT_STICKY | ||
} | ||
|
||
|
||
private fun makeNotif(taskName: String, formattedTime: String): Notification { | ||
|
||
val notifBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) | ||
|
||
return notifBuilder | ||
.setSmallIcon(R.mipmap.ic_launcher) | ||
.setContentTitle(taskName) | ||
.setContentText(formattedTime) | ||
.setOnlyAlertOnce(true) | ||
.addAction(makeStopAction()) | ||
.build() | ||
} | ||
|
||
private fun makeStopAction(): NotificationCompat.Action { | ||
val stopIntent = Intent(this, this::class.java) | ||
stopIntent.action = ACTION_STOP | ||
val stopPendingIntent = PendingIntent | ||
.getService(this, 1, stopIntent, PendingIntent.FLAG_IMMUTABLE) | ||
|
||
return NotificationCompat | ||
.Action(R.drawable.baseline_stop_24, "Stop", stopPendingIntent) | ||
} | ||
|
||
override fun onBind(intent: Intent?): IBinder? = null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<vector android:height="24dp" android:tint="#000000" | ||
android:viewportHeight="24" android:viewportWidth="24" | ||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<path android:fillColor="@android:color/white" android:pathData="M6,6h12v12H6z"/> | ||
</vector> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import 'package:flutter/services.dart'; | ||
|
||
class NotificationManager { | ||
final void Function() onTimerDissmissed; | ||
|
||
NotificationManager(this.onTimerDissmissed) { | ||
channel.setMethodCallHandler((call) async { | ||
switch (call.method) { | ||
case "onTimerDismissed": | ||
onTimerDissmissed(); | ||
break; | ||
} | ||
}); | ||
} | ||
|
||
final channel = const MethodChannel('interval.qori.dev/notification'); | ||
|
||
void showTimer(String taskName, String formattedDuration) { | ||
channel.invokeMethod("showTimer", { | ||
"taskName": taskName, | ||
"formattedDuration": formattedDuration, | ||
}); | ||
} | ||
|
||
void dismissTimer() { | ||
channel.invokeMethod("dismissTimer"); | ||
} | ||
|
||
void setOnTimerDismissed(void Function() onTimerDissmissed) {} | ||
} |