From 5b19c422f2310425d11189c92afdc7c4974b9399 Mon Sep 17 00:00:00 2001
From: Nyra Sama <83142634+NyraSama@users.noreply.github.com>
Date: Tue, 9 Apr 2024 16:14:22 +0200
Subject: [PATCH] feat(android_notif): Add daily notification in android
platform and set the option in profile view (#1380)
---
yaki_mobile/android/app/build.gradle | 2 +-
.../android/app/src/main/AndroidManifest.xml | 6 +-
.../main/kotlin/com/xpeho/yaki/AlarmItem.kt | 8 +++
.../kotlin/com/xpeho/yaki/AlarmReceiver.kt | 55 +++++++++++++++++++
.../kotlin/com/xpeho/yaki/AlarmScheduler.kt | 6 ++
.../com/xpeho/yaki/AlarmSchedulerImpl.kt | 55 +++++++++++++++++++
.../kotlin/com/xpeho/yaki/MainActivity.kt | 46 +++++++++++++++-
yaki_mobile/assets/translations/en.json | 4 +-
yaki_mobile/assets/translations/fr.json | 4 +-
.../features/profile/profile.dart | 26 ++++++++-
yaki_mobile/pubspec.lock | 6 +-
yaki_mobile/pubspec.yaml | 2 +-
12 files changed, 210 insertions(+), 10 deletions(-)
create mode 100644 yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmItem.kt
create mode 100644 yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmReceiver.kt
create mode 100644 yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmScheduler.kt
create mode 100644 yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmSchedulerImpl.kt
diff --git a/yaki_mobile/android/app/build.gradle b/yaki_mobile/android/app/build.gradle
index a68fccf0c..ce0818fd3 100644
--- a/yaki_mobile/android/app/build.gradle
+++ b/yaki_mobile/android/app/build.gradle
@@ -53,7 +53,7 @@ android {
applicationId "com.xpeho.yaki"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion flutter.minSdkVersion
+ minSdkVersion 26
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/yaki_mobile/android/app/src/main/AndroidManifest.xml b/yaki_mobile/android/app/src/main/AndroidManifest.xml
index 60ee63240..5a44b9f9c 100644
--- a/yaki_mobile/android/app/src/main/AndroidManifest.xml
+++ b/yaki_mobile/android/app/src/main/AndroidManifest.xml
@@ -4,12 +4,14 @@
+
+
+
-
+
+
diff --git a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmItem.kt b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmItem.kt
new file mode 100644
index 000000000..fb876c8f4
--- /dev/null
+++ b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmItem.kt
@@ -0,0 +1,8 @@
+package com.xpeho.yaki
+
+import java.time.LocalDateTime
+
+data class AlarmItem(
+ val time: LocalDateTime,
+ val message: String,
+)
diff --git a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmReceiver.kt b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmReceiver.kt
new file mode 100644
index 000000000..ea1b863eb
--- /dev/null
+++ b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmReceiver.kt
@@ -0,0 +1,55 @@
+package com.xpeho.yaki
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.NotificationCompat
+import java.time.LocalDateTime
+
+class AlarmReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ val message = intent?.getStringExtra("message") ?: return
+
+ if (context != null) {
+ // Show a push notification
+ val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as
+ NotificationManager
+
+ val channel =
+ NotificationChannel(
+ "Alarm",
+ "Alarm",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager.createNotificationChannel(channel)
+
+ val notification =
+ NotificationCompat.Builder(context, "Alarm")
+ .setContentTitle("Reminder")
+ .setContentText(message)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .build()
+
+ notificationManager.notify(1, notification)
+
+ // Define the next alarm
+ if (intent.getBooleanExtra("repeat", false)) {
+ AlarmSchedulerImpl(context = context)
+ .schedule(
+ AlarmItem(
+ time =
+ LocalDateTime.now()
+ .plusDays(
+ 1
+ ),
+ message =
+ "It's time to do your daily declaration!"
+ )
+ )
+ }
+ }
+ }
+}
diff --git a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmScheduler.kt b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmScheduler.kt
new file mode 100644
index 000000000..af84047fa
--- /dev/null
+++ b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmScheduler.kt
@@ -0,0 +1,6 @@
+package com.xpeho.yaki
+
+interface AlarmScheduler {
+ fun schedule(item: AlarmItem)
+ fun cancel(item: AlarmItem)
+}
\ No newline at end of file
diff --git a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmSchedulerImpl.kt b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmSchedulerImpl.kt
new file mode 100644
index 000000000..f2dc0eab3
--- /dev/null
+++ b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/AlarmSchedulerImpl.kt
@@ -0,0 +1,55 @@
+package com.xpeho.yaki
+
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.provider.Settings
+import java.time.ZoneId
+
+class AlarmSchedulerImpl(private val context: Context) : AlarmScheduler {
+
+ private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+
+ override fun schedule(item: AlarmItem) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (!alarmManager.canScheduleExactAlarms()) {
+ val intent =
+ Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
+ data = Uri.parse("package:${context.packageName}")
+ }
+ context.startActivity(intent)
+ return
+ }
+ }
+
+ val intent =
+ Intent(context, AlarmReceiver::class.java).apply {
+ putExtra("message", item.message)
+ putExtra("repeat", true)
+ }
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ item.time.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000,
+ PendingIntent.getBroadcast(
+ context,
+ item.hashCode(),
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ }
+
+ override fun cancel(item: AlarmItem) {
+ alarmManager.cancel(
+ PendingIntent.getBroadcast(
+ context,
+ item.hashCode(),
+ Intent(context, AlarmReceiver::class.java),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ }
+}
diff --git a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/MainActivity.kt b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/MainActivity.kt
index 2b240f305..83eee20d3 100644
--- a/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/MainActivity.kt
+++ b/yaki_mobile/android/app/src/main/kotlin/com/xpeho/yaki/MainActivity.kt
@@ -1,6 +1,50 @@
package com.xpeho.yaki
+import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
+import java.time.LocalDateTime
-class MainActivity: FlutterActivity() {
+class MainActivity : FlutterActivity() {
+ private val CHANNEL = "com.xpeho.yaki/notification"
+
+ override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+ MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
+ call,
+ result ->
+ when (call.method) {
+ "scheduleNotifications" -> {
+ scheduleNotifications()
+ result.success(null)
+ }
+ "cancelNotifications" -> {
+ cancelNotifications()
+ result.success(null)
+ }
+ else -> result.notImplemented()
+ }
+ }
+ }
+
+ private fun scheduleNotifications() {
+ AlarmSchedulerImpl(context = this)
+ .schedule(
+ AlarmItem(
+ time = LocalDateTime.now().withHour(9).withMinute(0).withSecond(0),
+ message = "It's time to do your daily declaration!"
+ )
+ )
+ }
+
+ private fun cancelNotifications() {
+ AlarmSchedulerImpl(context = this)
+ .cancel(
+ AlarmItem(
+ time = LocalDateTime.now().withHour(9).withMinute(0).withSecond(0),
+ message = "It's time to do your daily declaration!"
+ )
+ )
+ }
}
diff --git a/yaki_mobile/assets/translations/en.json b/yaki_mobile/assets/translations/en.json
index af135c6dd..14e1e5739 100644
--- a/yaki_mobile/assets/translations/en.json
+++ b/yaki_mobile/assets/translations/en.json
@@ -153,5 +153,7 @@
"zero": "Just now",
"one": "Since {} minute",
"other": "Since {} minutes ago"
- }
+ },
+
+ "notificationStatus": "Daily notifications"
}
diff --git a/yaki_mobile/assets/translations/fr.json b/yaki_mobile/assets/translations/fr.json
index 1a01ce419..707a8c5b4 100644
--- a/yaki_mobile/assets/translations/fr.json
+++ b/yaki_mobile/assets/translations/fr.json
@@ -155,5 +155,7 @@
"zero": "À l'instant",
"one": "Depuis {} minute",
"other": "Depuis {} minutes"
- }
+ },
+
+ "notificationStatus": "Notifications journalières"
}
diff --git a/yaki_mobile/lib/presentation/features/profile/profile.dart b/yaki_mobile/lib/presentation/features/profile/profile.dart
index 1fd821118..224348f2e 100644
--- a/yaki_mobile/lib/presentation/features/profile/profile.dart
+++ b/yaki_mobile/lib/presentation/features/profile/profile.dart
@@ -106,7 +106,31 @@ class Profile extends ConsumerWidget {
),
],
),
- const SizedBox(height: 10),
+ const SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.only(left: 30.0),
+ child: Row(
+ children: [
+ const SizedBox(
+ width: 48,
+ child: Swap(
+ setActivated: true,
+ ),
+ ),
+ const SizedBox(width: 20),
+ Text(
+ tr('notificationStatus'),
+ style: const TextStyle(
+ color: kTextColor,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ fontFamily: 'SF Pro Rounded',
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 20),
InputText(
type: InputTextType.email,
label: tr('inputLabelFirstName'),
diff --git a/yaki_mobile/pubspec.lock b/yaki_mobile/pubspec.lock
index a4f0bbfa5..480831d37 100644
--- a/yaki_mobile/pubspec.lock
+++ b/yaki_mobile/pubspec.lock
@@ -1277,11 +1277,11 @@ packages:
dependency: "direct main"
description:
path: flutter
- ref: "0.9.0"
- resolved-ref: b7d6f308afc2e5e330f0a6acc71ea3c9331054a6
+ ref: "0.10.0"
+ resolved-ref: c3b9677ecbe15840e972351b0499bd70ceeb2ef5
url: "https://github.com/XPEHO/yaki_ui.git"
source: git
- version: "0.9.0"
+ version: "0.10.0"
yaml:
dependency: transitive
description:
diff --git a/yaki_mobile/pubspec.yaml b/yaki_mobile/pubspec.yaml
index a566bdad9..79360a82f 100644
--- a/yaki_mobile/pubspec.yaml
+++ b/yaki_mobile/pubspec.yaml
@@ -92,7 +92,7 @@ dependencies:
git:
url: https://github.com/XPEHO/yaki_ui.git
path: flutter
- ref: 0.9.0
+ ref: 0.10.0
collection: ^1.17.2
image_picker: ^1.0.4
path_provider: ^2.1.1