diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt index 53e3202..915bbb2 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt @@ -46,6 +46,8 @@ import com.javinator9889.handwashingreminder.gms.activity.ActivityHandler import com.javinator9889.handwashingreminder.gms.ads.AdLoader import com.javinator9889.handwashingreminder.gms.ads.AdsEnabler import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler +import com.javinator9889.handwashingreminder.jobs.alarms.Alarms +import com.javinator9889.handwashingreminder.notifications.NotificationsHandler import com.javinator9889.handwashingreminder.utils.* import com.javinator9889.handwashingreminder.utils.Preferences.ADS_ENABLED import com.javinator9889.handwashingreminder.utils.Preferences.APP_INIT_KEY @@ -102,40 +104,41 @@ class LauncherActivity : AppCompatActivity() { setContentView(R.layout.splash_screen) } - private fun showWelcomeScreenAsync() = lifecycleScope.launch(Dispatchers.Main) { - app.firebaseInitDeferred.await() - val isThereAnySpecialEvent = with(Firebase.remoteConfig) { - getBoolean(SPECIAL_EVENT) && !launchFromNotification - } - var sleepDuration = 0L - val animationLoaded = CompletableDeferred() - val fadeInAnimation = AnimationUtils.loadAnimation( - this@LauncherActivity, - android.R.anim.fade_in - ) - fadeInAnimation.duration = 300L - fadeInAnimation.setAnimationListener(object : - Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) {} - override fun onAnimationRepeat(animation: Animation?) {} - override fun onAnimationEnd(animation: Animation?) { - animationLoaded.complete(true) - logo.playAnimation() + private fun showWelcomeScreenAsync() = + lifecycleScope.launch(Dispatchers.Main) { + app.firebaseInitDeferred.await() + val isThereAnySpecialEvent = with(Firebase.remoteConfig) { + getBoolean(SPECIAL_EVENT) && !launchFromNotification } - }) - if (isThereAnySpecialEvent) { - logo.setAnimation(AnimatedResources.STAY_SAFE_STAY_HOME.res) - logo.addLottieOnCompositionLoadedListener { + var sleepDuration = 0L + val animationLoaded = CompletableDeferred() + val fadeInAnimation = AnimationUtils.loadAnimation( + this@LauncherActivity, + android.R.anim.fade_in + ) + fadeInAnimation.duration = 300L + fadeInAnimation.setAnimationListener(object : + Animation.AnimationListener { + override fun onAnimationStart(animation: Animation?) {} + override fun onAnimationRepeat(animation: Animation?) {} + override fun onAnimationEnd(animation: Animation?) { + animationLoaded.complete(true) + logo.playAnimation() + } + }) + if (isThereAnySpecialEvent) { + logo.setAnimation(AnimatedResources.STAY_SAFE_STAY_HOME.res) + logo.addLottieOnCompositionLoadedListener { + logo.startAnimation(fadeInAnimation) + sleepDuration = logo.duration + } + animationLoaded.await() + delay(sleepDuration) + } else { + logo.setImageResource(R.drawable.handwashing_app_logo) logo.startAnimation(fadeInAnimation) - sleepDuration = logo.duration } - animationLoaded.await() - delay(sleepDuration) - } else { - logo.setImageResource(R.drawable.handwashing_app_logo) - logo.startAnimation(fadeInAnimation) } - } override fun onActivityResult( requestCode: Int, @@ -148,7 +151,8 @@ class LauncherActivity : AppCompatActivity() { return } if (Ads.MODULE_NAME in splitInstallManager.installedModules && - sharedPreferences.getBoolean(ADS_ENABLED, true)) { + sharedPreferences.getBoolean(ADS_ENABLED, true) + ) { when (resultCode) { Activity.RESULT_OK -> { createPackageContext(packageName, 0).also { @@ -160,7 +164,8 @@ class LauncherActivity : AppCompatActivity() { } } if (sharedPreferences.getBoolean(APP_INIT_KEY, false) && - AppIntro.MODULE_NAME in splitInstallManager.installedModules) { + AppIntro.MODULE_NAME in splitInstallManager.installedModules + ) { data?.let { val launchIntent = Intent(data) createPackageContext(packageName, 0).also { @@ -190,7 +195,8 @@ class LauncherActivity : AppCompatActivity() { Timber.d("Required to install modules: $modules") if (modules.isEmpty()) { val intent = if (AppIntro.MODULE_NAME in installedModules && - !sharedPreferences.getBoolean(APP_INIT_KEY, false)) { + !sharedPreferences.getBoolean(APP_INIT_KEY, false) + ) { with(Intent()) { setClassName( BuildConfig.APPLICATION_ID, @@ -238,7 +244,8 @@ class LauncherActivity : AppCompatActivity() { private fun initAds(context: Context = app) { if (Ads.MODULE_NAME in splitInstallManager.installedModules && - sharedPreferences.getBoolean(ADS_ENABLED, true)) { + sharedPreferences.getBoolean(ADS_ENABLED, true) + ) { val className = "${Ads.PACKAGE_NAME}.${Ads .CLASS_NAME}\$${Ads.PROVIDER_NAME}" val adProvider = Class.forName(className).kotlin @@ -268,6 +275,8 @@ class LauncherActivity : AppCompatActivity() { app.firebaseInitDeferred.await() } Timber.d("Firebase initialized correctly") + Timber.d("Setting-up Firebase custom properties") + val propertiesJob = setupFirebasePropertiesAsync() Timber.d("Initializing Iconics") Iconics.init(this) Timber.d("Setting-up activity recognition") @@ -288,8 +297,19 @@ class LauncherActivity : AppCompatActivity() { Timber.d("Initializing Ads Provider") initAds() Timber.d("Adding periodic notifications if not enqueued yet") - Timber.d("Setting-up Firebase custom properties") - setupFirebasePropertiesAsync().join() + Timber.d("Creating alarms notification channels...") + for (alarm in Alarms.values()) { + Timber.d("Creating notification channel for ${alarm.identifier}") + NotificationsHandler( + context = this, + channelId = alarm.channelId, + channelName = getString(R.string.time_notification_channel_name), + channelDesc = getString(R.string.time_notification_channel_desc), + groupId = alarm.identifier, + groupName = getString(alarm.groupName) + ) + } + propertiesJob.join() } private fun setupFirebasePropertiesAsync() = lifecycleScope.launch { diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt index f2608c6..8892291 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt @@ -23,6 +23,7 @@ import android.content.ClipDescription import android.content.Intent import android.net.Uri import android.os.Bundle +import android.provider.Settings import androidx.annotation.StringRes import androidx.emoji.text.EmojiCompat import androidx.lifecycle.LifecycleOwner @@ -316,6 +317,27 @@ class SettingsLoader( Preferences.PERFORMANCE_ANIMATIONS, Ionicons.Icon.ion_battery_low ).also { deferreds.add(it) } + setupPreferenceAsync( + "notifications:settings", + onClickListener = { + val intent = when { + isAtLeast(AndroidVersion.O) -> Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName) + } + isAtLeast(AndroidVersion.LOLLIPOP) -> Intent("android.settings.APP_NOTIFICATION_SETTINGS").apply { + putExtra("app_package", requireContext().packageName) + putExtra("app_uid", requireContext().applicationInfo.uid) + } + else -> Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + addCategory(Intent.CATEGORY_DEFAULT) + data = Uri.parse("package: ${requireContext().packageName}") + } + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + requireContext().startActivity(intent) + true + } + ).also { deferreds.add(it) } deferreds.awaitAll() arePreferencesInitialized.set(true) } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt index 4353aa2..3e0593e 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt @@ -18,14 +18,36 @@ */ package com.javinator9889.handwashingreminder.jobs.alarms +import androidx.annotation.StringRes +import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.utils.Preferences enum class Alarms( val identifier: String, val code: Int, - val preferenceKey: String + val preferenceKey: String, + @StringRes val groupName: Int, + val channelId: String ) { - BREAKFAST_ALARM("alarms:breakfast", 0, Preferences.BREAKFAST_TIME), - LUNCH_ALARM("alarms:lunch", 1, Preferences.LUNCH_TIME), - DINNER_ALARM("alarms:dinner", 2, Preferences.DINNER_TIME) + BREAKFAST_ALARM( + "alarms:breakfast", + 0, + Preferences.BREAKFAST_TIME, + R.string.breakfast_notifications, + "notifications:breakfast" + ), + LUNCH_ALARM( + "alarms:lunch", + 1, + Preferences.LUNCH_TIME, + R.string.lunch_notifications, + "notifications:lunch" + ), + DINNER_ALARM( + "alarms:dinner", + 2, + Preferences.DINNER_TIME, + R.string.dinner_notifications, + "notifications:dinner" + ) } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt index 11a44f8..e48872b 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt @@ -21,13 +21,13 @@ package com.javinator9889.handwashingreminder.jobs.workers import android.content.Context import androidx.annotation.ArrayRes import androidx.annotation.StringRes +import androidx.core.app.NotificationCompat import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.application.HandwashingApplication import com.javinator9889.handwashingreminder.emoji.EmojiLoader import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.jobs.alarms.Alarms import com.javinator9889.handwashingreminder.notifications.NotificationsHandler -import com.javinator9889.handwashingreminder.utils.TIME_CHANNEL_ID import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.withContext @@ -46,9 +46,11 @@ abstract class ScheduledNotificationWorker(context: Context) { val emojiLoader = EmojiLoader.loadAsync(context) val notificationsHandler = NotificationsHandler( context = context, - channelId = TIME_CHANNEL_ID, + channelId = alarm.channelId, channelName = getString(R.string.time_notification_channel_name), - channelDesc = getString(R.string.time_notification_channel_desc) + channelDesc = getString(R.string.time_notification_channel_desc), + groupId = alarm.identifier, + groupName = getString(alarm.groupName) ) val emojiCompat = emojiLoader.await() var title = getText(titleRes) @@ -64,7 +66,8 @@ abstract class ScheduledNotificationWorker(context: Context) { largeIcon = R.drawable.handwashing_app_logo, title = title, content = content, - longContent = content + longContent = content, + priority = NotificationCompat.PRIORITY_MAX ) } Timber.d( diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/notifications/NotificationsHandler.kt b/app/src/main/java/com/javinator9889/handwashingreminder/notifications/NotificationsHandler.kt index e16ec20..a59953e 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/notifications/NotificationsHandler.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/notifications/NotificationsHandler.kt @@ -19,6 +19,7 @@ package com.javinator9889.handwashingreminder.notifications import android.app.NotificationChannel +import android.app.NotificationChannelGroup import android.app.NotificationManager import android.app.PendingIntent import android.content.Context @@ -31,7 +32,6 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.core.content.edit import androidx.preference.PreferenceManager import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.FAST_START_KEY @@ -47,20 +47,30 @@ class NotificationsHandler( private val context: Context, private val channelId: String, private val channelName: String = "", - private val channelDesc: String = "" + private val channelDesc: String = "", + private val groupId: String = "", + private val groupName: String = "" ) { private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) private val notificationId = 1 private val vibrationPattern = longArrayOf(300L, 300L, 300L, 300L) + private val manager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as + NotificationManager init { - if (isNotificationChannelCreated() || createChannelRequired()) { - createNotificationChannel() - preferences.edit { - putBoolean(Preferences.CREATE_CHANNEL_KEY, false) - } + if (groupId.isNotEmpty() && groupName.isNotEmpty() + && isAtLeast(AndroidVersion.O) + ) { + manager.createNotificationChannelGroup( + NotificationChannelGroup( + groupId, + groupName + ) + ) } + createNotificationChannel(groupId) } fun createNotification( @@ -165,7 +175,7 @@ class NotificationsHandler( } } - private fun createNotificationChannel() { + private fun createNotificationChannel(groupId: String = "") { if (isAtLeast(AndroidVersion.O)) { val importance = NotificationManager.IMPORTANCE_HIGH val that = this @@ -175,19 +185,15 @@ class NotificationsHandler( description = channelDesc vibrationPattern = that.vibrationPattern enableVibration(true) + if (groupId.isNotEmpty()) + group = groupId } - val notificationManager: NotificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as - NotificationManager - notificationManager.createNotificationChannel(channel) + manager.createNotificationChannel(channel) } } private fun isNotificationChannelCreated(): Boolean { if (isAtLeast(AndroidVersion.O)) { - val manager = context - .getSystemService(Context.NOTIFICATION_SERVICE) as - NotificationManager val channel = manager.getNotificationChannel(channelId) channel?.let { return it.importance != NotificationManager.IMPORTANCE_NONE @@ -198,7 +204,6 @@ class NotificationsHandler( } } - private fun createChannelRequired(): Boolean { - return preferences.getBoolean(Preferences.CREATE_CHANNEL_KEY, true) - } + private fun createChannelRequired() = + preferences.getBoolean(Preferences.CREATE_CHANNEL_KEY, true) } \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4bb1a48..ca5ad10 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -68,6 +68,9 @@ ¿30 segundos por una buena salud? ¡Dónde firmo! Solo lávate las manos 😉💦👏 + Recordatorios en el desayuno + Recordatorios en la comida + Recordatorios en la cenas ¡Hora de comer y lavarse las manos! 🍱 @@ -291,4 +294,5 @@ Ten en cuenta que esto puede afectar tanto al rendimiento como a la batería No se mostrará ninguna animación Error al cargar información sobre las noticias + Ajustes de notificaciones diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f6df4e1..b08dcaf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,6 +69,7 @@ 30 seconds for good health? Where do I sign! Just wash your hands 😉💦👏 + Breakfast reminders Time to lunch and wash your hands! 🍱 @@ -79,6 +80,7 @@ If you have gone to the street is even more important to wash your hands before having lunch 💦👏 + Lunch reminders Let\'s have a clean dinner 🥗 The day is finishing and you can do it fully by @@ -88,6 +90,7 @@ Do you know that washing your hands can keep you safe from more than 15 diseases? That\'s a lot 😮 + Dinner reminders Let\'s learn how to wash our hands Join me in this trip @@ -319,4 +322,5 @@ Keep in mind that it can affect both performance and battery life No animations will be played Error while loading news data + Edit notification settings diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index f1e5a8e..a091e21 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -49,6 +49,11 @@ android:key="app:dinner" android:selectAllOnFocus="true" android:singleLine="true" /> + + diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt index 4ed510e..59016de 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt @@ -19,12 +19,12 @@ package com.javinator9889.handwashingreminder.appintro.timeconfig import android.view.View +import android.widget.ImageView import android.widget.TextView import androidx.annotation.LayoutRes import androidx.cardview.widget.CardView import coil.api.load import com.javinator9889.handwashingreminder.appintro.R -import com.javinator9889.handwashingreminder.graphics.RecyclingImageView import com.javinator9889.handwashingreminder.utils.TimeConfig import com.javinator9889.handwashingreminder.utils.notNull import com.mikepenz.fastadapter.FastAdapter @@ -52,7 +52,7 @@ class TimeConfigItem( private val hours: TextView = view.findViewById(R.id.hours) private val ddot: TextView = view.findViewById(R.id.ddot) private val minutes: TextView = view.findViewById(R.id.minutes) - private val image: RecyclingImageView = view.findViewById(R.id.infoImage) + private val image: ImageView = view.findViewById(R.id.infoImage) private val clockIcon: IconicsImageView = view.findViewById(R.id.clockIcon) val cardView: CardView = view.findViewById(R.id.timeCard) @@ -71,7 +71,6 @@ class TimeConfigItem( else -> null }.notNull { image.load(it) - image.savedDrawableRes = it } } @@ -80,7 +79,7 @@ class TimeConfigItem( hours.text = null ddot.text = null minutes.text = null - image.onDetachedFromWindow() + image.setImageDrawable(null) clockIcon.icon = null } } diff --git a/appintro/src/main/res/layout/time_card_view.xml b/appintro/src/main/res/layout/time_card_view.xml index 2ba32f4..9b95689 100644 --- a/appintro/src/main/res/layout/time_card_view.xml +++ b/appintro/src/main/res/layout/time_card_view.xml @@ -60,7 +60,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/title"> -