From 5fe085013091db7469c843f39c2f4ec5d85fed9d Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sat, 26 Dec 2020 13:55:52 -0300 Subject: [PATCH 01/15] project: Update dependencies to latest --- app/build.gradle | 38 ++++++++++++++---------- build.gradle | 8 +++-- gradle/wrapper/gradle-wrapper.properties | 4 +-- settings.gradle | 1 + 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5ca4e427..5eeb4f92 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,30 +34,36 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - - // Enables data binding. buildFeatures { dataBinding true } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.core:core-ktx:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-rc01' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation "androidx.appcompat:appcompat:1.2.0" + implementation "androidx.core:core-ktx:1.3.2" + implementation "androidx.fragment:fragment-ktx:1.2.5" + implementation "androidx.constraintlayout:constraintlayout:2.0.4" + implementation "androidx.legacy:legacy-support-v4:1.0.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + + testImplementation "junit:junit:4.13.1" + androidTestImplementation "androidx.test:runner:1.3.0" + androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0" //for fcm - implementation 'com.google.firebase:firebase-core:17.0.0' - implementation 'com.google.firebase:firebase-iid:19.0.1' - implementation 'com.google.firebase:firebase-messaging:19.0.1' - implementation 'android.arch.work:work-runtime:1.0.1' + implementation "com.google.firebase:firebase-core:18.0.0" + implementation "com.google.firebase:firebase-iid:21.0.1" + implementation "com.google.firebase:firebase-messaging:21.0.1" + implementation "android.arch.work:work-runtime:1.0.1" } diff --git a/build.gradle b/build.gradle index edab77f3..49bd34e1 100644 --- a/build.gradle +++ b/build.gradle @@ -16,14 +16,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.4.21' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -41,3 +41,7 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + +ext { + lifecycle_version = "2.2.0" +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 47a3857d..e346567c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Aug 28 16:34:52 PDT 2019 +#Sat Dec 26 13:10:10 BRT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/settings.gradle b/settings.gradle index e14c9a6d..c66fc0ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,3 +16,4 @@ */ include ':app' +rootProject.name = "Egg Timer" \ No newline at end of file From 12dd95f097f01cac2ba7cee4f95a44a712d99484 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sat, 26 Dec 2020 13:56:56 -0300 Subject: [PATCH 02/15] fix: Delegate ViewModel instantiation --- .../android/eggtimernotifications/ui/EggTimerFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt index 0802dc0f..7e196120 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt @@ -27,6 +27,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.databinding.FragmentEggTimerBinding @@ -34,6 +35,8 @@ import com.google.firebase.messaging.FirebaseMessaging class EggTimerFragment : Fragment() { + private val viewModel: EggTimerViewModel by viewModels() + private val TOPIC = "breakfast" override fun onCreateView( @@ -45,8 +48,6 @@ class EggTimerFragment : Fragment() { inflater, R.layout.fragment_egg_timer, container, false ) - val viewModel = ViewModelProvider(this).get(EggTimerViewModel::class.java) - binding.eggTimerViewModel = viewModel binding.lifecycleOwner = this.viewLifecycleOwner From 56e5ea0b826df7d5c3c03bed41dcbee31580f367 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sat, 26 Dec 2020 15:46:44 -0300 Subject: [PATCH 03/15] feat: Create the notification channel --- .../ui/EggTimerFragment.kt | 34 ++++++++++++++----- .../ui/EggTimerViewModel.kt | 9 +++++ .../{BindingUtils.kt => BindingAdapters.kt} | 0 ...otificationUtils.kt => NotificationExt.kt} | 15 +++++--- 4 files changed, 44 insertions(+), 14 deletions(-) rename app/src/main/java/com/example/android/eggtimernotifications/util/{BindingUtils.kt => BindingAdapters.kt} (100%) rename app/src/main/java/com/example/android/eggtimernotifications/util/{NotificationUtils.kt => NotificationExt.kt} (80%) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt index 7e196120..5bea79e5 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt @@ -24,14 +24,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast +import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.lifecycle.ViewModelProvider import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.databinding.FragmentEggTimerBinding -import com.google.firebase.messaging.FirebaseMessaging class EggTimerFragment : Fragment() { @@ -42,7 +40,7 @@ class EggTimerFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val binding: FragmentEggTimerBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_egg_timer, container, false @@ -51,16 +49,34 @@ class EggTimerFragment : Fragment() { binding.eggTimerViewModel = viewModel binding.lifecycleOwner = this.viewLifecycleOwner - // TODO: Step 1.7 call create channel + createChannel( + getString(R.string.egg_notification_channel_id), + getString(R.string.egg_notification_channel_name) + ) return binding.root } private fun createChannel(channelId: String, channelName: String) { - // TODO: Step 1.6 START create a channel - - // TODO: Step 1.6 END create a channel - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context?.let { + NotificationChannel( + channelId, + channelName, + NotificationManager.IMPORTANCE_LOW + ).apply { + enableLights(true) + lightColor = Color.RED + enableVibration(true) + description = "Time for breakfast" + }.run { + val notificationManager = + ContextCompat.getSystemService(it, NotificationManager::class.java) + as NotificationManager + notificationManager.createNotificationChannel(this) + } + } + } } companion object { diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt index 934306f1..ac690649 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt @@ -119,6 +119,15 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { val triggerTime = SystemClock.elapsedRealtime() + selectedInterval // TODO: Step 1.5 get an instance of NotificationManager and call sendNotification + val notificationManager = + ContextCompat.getSystemService( + app, + NotificationManager::class.java + ) as NotificationManager + notificationManager.sendNotification( + app.getString(R.string.timer_running), + app + ) // TODO: Step 1.15 call cancel notification diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/BindingUtils.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/BindingAdapters.kt similarity index 100% rename from app/src/main/java/com/example/android/eggtimernotifications/util/BindingUtils.kt rename to app/src/main/java/com/example/android/eggtimernotifications/util/BindingAdapters.kt diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationUtils.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt similarity index 80% rename from app/src/main/java/com/example/android/eggtimernotifications/util/NotificationUtils.kt rename to app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index 2f9e2a2e..5382d85f 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationUtils.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -22,6 +22,8 @@ import android.content.Context import android.content.Intent import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.SnoozeReceiver @@ -48,13 +50,17 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext // TODO: Step 2.2 add snooze action - // TODO: Step 1.2 get an instance of NotificationCompat.Builder // Build the notification + val notificationBuilder = NotificationCompat.Builder( + applicationContext, + applicationContext.getString(R.string.egg_notification_channel_id) + ) + .setSmallIcon(R.drawable.egg_icon) + .setContentTitle(applicationContext.getString(R.string.notification_title)) + .setContentText(messageBody) // TODO: Step 1.8 use the new 'breakfast' notification channel - // TODO: Step 1.3 set title, text and icon to builder - // TODO: Step 1.13 set content intent // TODO: Step 2.1 add style to builder @@ -63,8 +69,7 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext // TODO: Step 2.5 set priority - // TODO: Step 1.4 call notify - + notify(NOTIFICATION_ID, notificationBuilder.build()) } // TODO: Step 1.14 Cancel all notifications From 310bb4c83f4fe731240e2dd4e952d10886a66736 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sat, 26 Dec 2020 16:23:21 -0300 Subject: [PATCH 04/15] feat Use AlarmManager to deliver the notification when egg's ready --- .../receiver/AlarmReceiver.kt | 14 ++--- .../ui/EggTimerFragment.kt | 58 +++++-------------- .../ui/EggTimerViewModel.kt | 28 ++++----- .../util/ContextCompatExt.kt | 10 ++++ .../util/NotificationExt.kt | 35 ++++++++--- 5 files changed, 69 insertions(+), 76 deletions(-) create mode 100644 app/src/main/java/com/example/android/eggtimernotifications/util/ContextCompatExt.kt diff --git a/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt b/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt index 7313457c..f567475a 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt @@ -16,23 +16,19 @@ package com.example.android.eggtimernotifications.receiver -import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.widget.Toast -import androidx.core.content.ContextCompat import com.example.android.eggtimernotifications.R +import com.example.android.eggtimernotifications.util.getNotificationManager import com.example.android.eggtimernotifications.util.sendNotification class AlarmReceiver: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - // TODO: Step 1.10 [Optional] remove toast - Toast.makeText(context, context.getText(R.string.eggs_ready), Toast.LENGTH_SHORT).show() - - // TODO: Step 1.9 add call to sendNotification - + context.getNotificationManager().sendNotification( + context.getString(R.string.eggs_ready), + context.applicationContext + ) } - } \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt index 5bea79e5..73a2b9ff 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt @@ -16,20 +16,16 @@ package com.example.android.eggtimernotifications.ui -import android.app.NotificationChannel -import android.app.NotificationManager -import android.graphics.Color -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat -import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.databinding.FragmentEggTimerBinding +import com.example.android.eggtimernotifications.util.createChannel +import com.example.android.eggtimernotifications.util.getNotificationManager class EggTimerFragment : Fragment() { @@ -40,42 +36,20 @@ class EggTimerFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View { - - val binding: FragmentEggTimerBinding = DataBindingUtil.inflate( - inflater, R.layout.fragment_egg_timer, container, false - ) - - binding.eggTimerViewModel = viewModel - binding.lifecycleOwner = this.viewLifecycleOwner - - createChannel( - getString(R.string.egg_notification_channel_id), - getString(R.string.egg_notification_channel_name) - ) - - return binding.root - } - - private fun createChannel(channelId: String, channelName: String) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context?.let { - NotificationChannel( - channelId, - channelName, - NotificationManager.IMPORTANCE_LOW - ).apply { - enableLights(true) - lightColor = Color.RED - enableVibration(true) - description = "Time for breakfast" - }.run { - val notificationManager = - ContextCompat.getSystemService(it, NotificationManager::class.java) - as NotificationManager - notificationManager.createNotificationChannel(this) - } - } + ): View = FragmentEggTimerBinding.inflate(inflater, container, false) + .apply { + eggTimerViewModel = viewModel + lifecycleOwner = viewLifecycleOwner + } + .root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.run{ + getNotificationManager().createChannel( + getString(R.string.egg_notification_channel_id), + getString(R.string.egg_notification_channel_name) + ) } } diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt index ac690649..2db1425a 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt @@ -16,18 +16,23 @@ package com.example.android.eggtimernotifications.ui -import android.app.* +import android.app.AlarmManager +import android.app.Application +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.CountDownTimer import android.os.SystemClock import androidx.core.app.AlarmManagerCompat -import androidx.core.content.ContextCompat -import androidx.lifecycle.* -import com.example.android.eggtimernotifications.receiver.AlarmReceiver +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.example.android.eggtimernotifications.R -import com.example.android.eggtimernotifications.util.sendNotification -import kotlinx.coroutines.* +import com.example.android.eggtimernotifications.receiver.AlarmReceiver +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { @@ -118,17 +123,6 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { } val triggerTime = SystemClock.elapsedRealtime() + selectedInterval - // TODO: Step 1.5 get an instance of NotificationManager and call sendNotification - val notificationManager = - ContextCompat.getSystemService( - app, - NotificationManager::class.java - ) as NotificationManager - notificationManager.sendNotification( - app.getString(R.string.timer_running), - app - ) - // TODO: Step 1.15 call cancel notification AlarmManagerCompat.setExactAndAllowWhileIdle( diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/ContextCompatExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/ContextCompatExt.kt new file mode 100644 index 00000000..e2fb0961 --- /dev/null +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/ContextCompatExt.kt @@ -0,0 +1,10 @@ +package com.example.android.eggtimernotifications.util + +import android.app.NotificationManager +import android.content.Context +import androidx.core.content.ContextCompat.getSystemService + +fun Context.getNotificationManager(): NotificationManager = getSystemService( + this, + NotificationManager::class.java +) as NotificationManager \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index 5382d85f..2df0a70d 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -16,17 +16,13 @@ package com.example.android.eggtimernotifications.util +import android.app.NotificationChannel import android.app.NotificationManager -import android.app.PendingIntent import android.content.Context -import android.content.Intent -import android.graphics.BitmapFactory +import android.graphics.Color +import android.os.Build import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import androidx.core.content.ContextCompat -import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R -import com.example.android.eggtimernotifications.receiver.SnoozeReceiver // Notification ID. private val NOTIFICATION_ID = 0 @@ -39,7 +35,10 @@ private val FLAGS = 0 * * @param context, activity context. */ -fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) { +fun NotificationManager.sendNotification( + messageBody: String, + applicationContext: Context, +) { // Create the content intent for the notification, which launches // this activity // TODO: Step 1.11 create intent @@ -72,4 +71,24 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext notify(NOTIFICATION_ID, notificationBuilder.build()) } +fun NotificationManager.createChannel( + channelId: String, + channelName: String, +) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel( + channelId, + channelName, + NotificationManager.IMPORTANCE_LOW + ).apply { + enableLights(true) + lightColor = Color.RED + enableVibration(true) + description = "Time for breakfast" + }.run { + createNotificationChannel(this) + } + } +} + // TODO: Step 1.14 Cancel all notifications From 9938a7709b4f8e477ebf295752aa500c9502eb82 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sun, 27 Dec 2020 12:04:47 -0300 Subject: [PATCH 05/15] feat: Add PendingIntent to let MainActivity handle notification --- .../util/NotificationExt.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index 2df0a70d..d782c86c 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -18,10 +18,13 @@ package com.example.android.eggtimernotifications.util import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.content.Context +import android.content.Intent import android.graphics.Color import android.os.Build import androidx.core.app.NotificationCompat +import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R // Notification ID. @@ -29,7 +32,6 @@ private val NOTIFICATION_ID = 0 private val REQUEST_CODE = 0 private val FLAGS = 0 -// TODO: Step 1.1 extension function to send messages (GIVEN) /** * Builds and delivers the notification. * @@ -39,14 +41,15 @@ fun NotificationManager.sendNotification( messageBody: String, applicationContext: Context, ) { - // Create the content intent for the notification, which launches - // this activity - // TODO: Step 1.11 create intent - - // TODO: Step 1.12 create PendingIntent + val contentIntent = Intent(applicationContext, MainActivity::class.java) + val contentPendingIntent = PendingIntent.getActivity( + applicationContext, + REQUEST_CODE, + contentIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ) // TODO: Step 2.0 add style - // TODO: Step 2.2 add snooze action // Build the notification @@ -57,15 +60,12 @@ fun NotificationManager.sendNotification( .setSmallIcon(R.drawable.egg_icon) .setContentTitle(applicationContext.getString(R.string.notification_title)) .setContentText(messageBody) + .setContentIntent(contentPendingIntent) + .setAutoCancel(true) // TODO: Step 1.8 use the new 'breakfast' notification channel - - // TODO: Step 1.13 set content intent - // TODO: Step 2.1 add style to builder - // TODO: Step 2.3 add snooze action - // TODO: Step 2.5 set priority notify(NOTIFICATION_ID, notificationBuilder.build()) From 1d24e65b387509b9235ac85f8bac1cbef9858fdf Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sun, 27 Dec 2020 12:55:25 -0300 Subject: [PATCH 06/15] feat: Cancelling previous notifications --- .../android/eggtimernotifications/ui/EggTimerViewModel.kt | 5 +++-- .../android/eggtimernotifications/util/NotificationExt.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt index 2db1425a..459e1fdf 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt @@ -30,6 +30,8 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.AlarmReceiver +import com.example.android.eggtimernotifications.util.cancelNotifications +import com.example.android.eggtimernotifications.util.getNotificationManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -62,7 +64,6 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { val isAlarmOn: LiveData get() = _alarmOn - private lateinit var timer: CountDownTimer init { @@ -123,7 +124,7 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { } val triggerTime = SystemClock.elapsedRealtime() + selectedInterval - // TODO: Step 1.15 call cancel notification + app.applicationContext.getNotificationManager().cancelNotifications() AlarmManagerCompat.setExactAndAllowWhileIdle( alarmManager, diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index d782c86c..133234a1 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -91,4 +91,4 @@ fun NotificationManager.createChannel( } } -// TODO: Step 1.14 Cancel all notifications +fun NotificationManager.cancelNotifications() = cancelAll() From b9f1db0f1613082e7732f7c3741d7c53523c4c44 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sun, 27 Dec 2020 13:36:02 -0300 Subject: [PATCH 07/15] feat: Add a BigPictureStyle to the notification --- .gitignore | 32 ++++++------------- .../util/NotificationExt.kt | 13 +++++++- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index d7d6aa00..ec851b00 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,10 @@ -start/.gradle/5.1.1/gc.properties -start/.gradle/5.1.1/executionHistory/executionHistory.bin -start/.gradle/5.1.1/executionHistory/executionHistory.lock -start/.gradle/5.1.1/fileChanges/last-build.bin -start/.gradle/5.1.1/fileHashes/fileHashes.bin -start/.gradle/5.1.1/fileHashes/fileHashes.lock -start/.gradle/5.4.1/gc.properties -start/.gradle/5.4.1/executionHistory/executionHistory.bin -start/.gradle/5.4.1/executionHistory/executionHistory.lock -start/.gradle/5.4.1/fileChanges/last-build.bin -start/.gradle/5.4.1/fileContent/fileContent.lock -start/.gradle/5.4.1/fileHashes/fileHashes.bin -start/.gradle/5.4.1/fileHashes/fileHashes.lock -start/.gradle/5.4.1/fileHashes/resourceHashesCache.bin -start/.gradle/5.4.1/javaCompile/classAnalysis.bin -start/.gradle/5.4.1/javaCompile/jarAnalysis.bin -start/.gradle/5.4.1/javaCompile/javaCompile.lock -start/.gradle/5.4.1/javaCompile/taskHistory.bin -start/.gradle/buildOutputCleanup/buildOutputCleanup.lock -start/.gradle/buildOutputCleanup/cache.properties -start/.gradle/buildOutputCleanup/outputFiles.bin -start/.gradle/vcs-1/gc.properties +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties/ diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index 133234a1..b12e62a8 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -21,6 +21,7 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.graphics.BitmapFactory import android.graphics.Color import android.os.Build import androidx.core.app.NotificationCompat @@ -49,9 +50,17 @@ fun NotificationManager.sendNotification( PendingIntent.FLAG_UPDATE_CURRENT ) - // TODO: Step 2.0 add style // TODO: Step 2.2 add snooze action + val eggImage = BitmapFactory.decodeResource( + applicationContext.resources, + R.drawable.cooked_egg + ) + + val style = NotificationCompat.BigPictureStyle() + .bigPicture(eggImage) + .bigLargeIcon(null) + // Build the notification val notificationBuilder = NotificationCompat.Builder( applicationContext, @@ -62,6 +71,8 @@ fun NotificationManager.sendNotification( .setContentText(messageBody) .setContentIntent(contentPendingIntent) .setAutoCancel(true) + .setStyle(style) + .setLargeIcon(eggImage) // TODO: Step 1.8 use the new 'breakfast' notification channel // TODO: Step 2.1 add style to builder From 1591d88ce2f5135886d13e8fd8459b0aa9e42bab Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Sun, 27 Dec 2020 13:48:08 -0300 Subject: [PATCH 08/15] fix: Ensure the PendingIntent doesn't go to the default task --- app/src/main/AndroidManifest.xml | 6 +++++- .../android/eggtimernotifications/util/NotificationExt.kt | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 04993f0f..99db6fd0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,7 +26,11 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - + diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index b12e62a8..c49f57e6 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -43,6 +43,9 @@ fun NotificationManager.sendNotification( applicationContext: Context, ) { val contentIntent = Intent(applicationContext, MainActivity::class.java) + .apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } val contentPendingIntent = PendingIntent.getActivity( applicationContext, REQUEST_CODE, From e4a94dbcad95da621be96f5c89135e692d60a96c Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Mon, 28 Dec 2020 13:42:19 -0300 Subject: [PATCH 09/15] feat: Add a Snooze action to reeschedule the notification --- .../receiver/SnoozeReceiver.kt | 4 ++++ .../util/NotificationExt.kt | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt b/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt index ceea1381..c82b3683 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt @@ -26,6 +26,8 @@ import android.os.SystemClock import android.text.format.DateUtils import androidx.core.app.AlarmManagerCompat import androidx.core.content.ContextCompat +import com.example.android.eggtimernotifications.util.cancelNotifications +import com.example.android.eggtimernotifications.util.getNotificationManager class SnoozeReceiver: BroadcastReceiver() { private val REQUEST_CODE = 0 @@ -47,6 +49,8 @@ class SnoozeReceiver: BroadcastReceiver() { triggerTime, notifyPendingIntent ) + + context.applicationContext.getNotificationManager().cancelNotifications() } } \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index c49f57e6..087b6590 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -25,13 +25,14 @@ import android.graphics.BitmapFactory import android.graphics.Color import android.os.Build import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R +import com.example.android.eggtimernotifications.receiver.SnoozeReceiver // Notification ID. private val NOTIFICATION_ID = 0 private val REQUEST_CODE = 0 -private val FLAGS = 0 /** * Builds and delivers the notification. @@ -53,7 +54,13 @@ fun NotificationManager.sendNotification( PendingIntent.FLAG_UPDATE_CURRENT ) - // TODO: Step 2.2 add snooze action + val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java) + val snoozePendingIntent = PendingIntent.getBroadcast( + applicationContext, + REQUEST_CODE, + snoozeIntent, + PendingIntent.FLAG_ONE_SHOT + ) val eggImage = BitmapFactory.decodeResource( applicationContext.resources, @@ -76,6 +83,11 @@ fun NotificationManager.sendNotification( .setAutoCancel(true) .setStyle(style) .setLargeIcon(eggImage) + .addAction( + R.drawable.egg_icon, + applicationContext.getString(R.string.snooze), + snoozePendingIntent + ) // TODO: Step 1.8 use the new 'breakfast' notification channel // TODO: Step 2.1 add style to builder From 97bf15f86de5b691212eef58072ebc64ba050d60 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Tue, 29 Dec 2020 15:09:49 -0300 Subject: [PATCH 10/15] feat: Send notifications with High priority --- .../receiver/AlarmReceiver.kt | 2 +- .../receiver/SnoozeReceiver.kt | 3 +- .../ui/EggTimerViewModel.kt | 2 + .../util/NotificationExt.kt | 53 +++++++++++-------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt b/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt index f567475a..a672aff2 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/receiver/AlarmReceiver.kt @@ -28,7 +28,7 @@ class AlarmReceiver: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { context.getNotificationManager().sendNotification( context.getString(R.string.eggs_ready), - context.applicationContext + context ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt b/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt index c82b3683..0c15b3ad 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/receiver/SnoozeReceiver.kt @@ -29,6 +29,7 @@ import androidx.core.content.ContextCompat import com.example.android.eggtimernotifications.util.cancelNotifications import com.example.android.eggtimernotifications.util.getNotificationManager +// TODO: Improve it - Remove this DRY code class SnoozeReceiver: BroadcastReceiver() { private val REQUEST_CODE = 0 @@ -50,7 +51,7 @@ class SnoozeReceiver: BroadcastReceiver() { notifyPendingIntent ) - context.applicationContext.getNotificationManager().cancelNotifications() + context.getNotificationManager().cancelNotifications() } } \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt index 459e1fdf..e8270535 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt @@ -74,6 +74,7 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { PendingIntent.FLAG_NO_CREATE ) != null + // TODO: Improve it - Remove this DRY code notifyPendingIntent = PendingIntent.getBroadcast( getApplication(), REQUEST_CODE, @@ -124,6 +125,7 @@ class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) { } val triggerTime = SystemClock.elapsedRealtime() + selectedInterval + // TODO: Improve it - Remove this DRY code app.applicationContext.getNotificationManager().cancelNotifications() AlarmManagerCompat.setExactAndAllowWhileIdle( diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index 087b6590..f4056cb2 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -25,45 +25,44 @@ import android.graphics.BitmapFactory import android.graphics.Color import android.os.Build import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat +import androidx.core.app.NotificationManagerCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.SnoozeReceiver -// Notification ID. private val NOTIFICATION_ID = 0 private val REQUEST_CODE = 0 /** * Builds and delivers the notification. * - * @param context, activity context. + * @param context activity context. */ fun NotificationManager.sendNotification( messageBody: String, - applicationContext: Context, + context: Context, ) { - val contentIntent = Intent(applicationContext, MainActivity::class.java) + val contentIntent = Intent(context, MainActivity::class.java) .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val contentPendingIntent = PendingIntent.getActivity( - applicationContext, + context, REQUEST_CODE, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT ) - val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java) + val snoozeIntent = Intent(context, SnoozeReceiver::class.java) val snoozePendingIntent = PendingIntent.getBroadcast( - applicationContext, + context, REQUEST_CODE, snoozeIntent, PendingIntent.FLAG_ONE_SHOT ) val eggImage = BitmapFactory.decodeResource( - applicationContext.resources, + context.resources, R.drawable.cooked_egg ) @@ -71,30 +70,38 @@ fun NotificationManager.sendNotification( .bigPicture(eggImage) .bigLargeIcon(null) - // Build the notification - val notificationBuilder = NotificationCompat.Builder( - applicationContext, - applicationContext.getString(R.string.egg_notification_channel_id) + NotificationCompat.Builder( + context, + context.getString(R.string.egg_notification_channel_id) ) .setSmallIcon(R.drawable.egg_icon) - .setContentTitle(applicationContext.getString(R.string.notification_title)) + .setContentTitle(context.getString(R.string.notification_title)) .setContentText(messageBody) .setContentIntent(contentPendingIntent) + // auto dissmiss notification from status bar .setAutoCancel(true) + // style notifications with Big Style containing a big image .setStyle(style) + // use this to show a small icon when collapsing the notification .setLargeIcon(eggImage) + // snooze action to reeschedule the notification .addAction( R.drawable.egg_icon, - applicationContext.getString(R.string.snooze), + context.getString(R.string.snooze), snoozePendingIntent ) - - // TODO: Step 1.8 use the new 'breakfast' notification channel - // TODO: Step 2.1 add style to builder - // TODO: Step 2.3 add snooze action - // TODO: Step 2.5 set priority - - notify(NOTIFICATION_ID, notificationBuilder.build()) + // priority defines the level of user interruption + // High priority makes a sound and appears as a heads up notification + // Default priority makes a sound + // Low priority makes no sound + // Min priority makes no sound and does not appear in the status bar + .setPriority(NotificationManagerCompat.IMPORTANCE_HIGH) + // This information about your notification category is used by the system to make decisions about displaying your notification when the device is in Do Not Disturb mode. + // https://developer.android.com/training/notify-user/build-notification#system-category + .setCategory(NotificationCompat.CATEGORY_REMINDER) + .run { + notify(NOTIFICATION_ID, this.build()) + } } fun NotificationManager.createChannel( @@ -105,7 +112,7 @@ fun NotificationManager.createChannel( NotificationChannel( channelId, channelName, - NotificationManager.IMPORTANCE_LOW + NotificationManager.IMPORTANCE_HIGH ).apply { enableLights(true) lightColor = Color.RED From fcb50b0d41d8fa375be099d949114107120d8a7b Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Tue, 29 Dec 2020 15:51:51 -0300 Subject: [PATCH 11/15] feat: Show an urgent message --- app/src/main/AndroidManifest.xml | 52 ++++++------- .../eggtimernotifications/MainActivity.kt | 2 +- .../important/ImportantActivity.kt | 14 ++++ .../{ui => timer}/EggTimerFragment.kt | 2 +- .../{ui => timer}/EggTimerViewModel.kt | 2 +- .../util/NotificationExt.kt | 16 +++- .../main/res/layout/fragment_egg_timer.xml | 4 +- .../main/res/layout/important_activity.xml | 73 +++++++++++++++++++ app/src/main/res/values/dimens.xml | 4 + app/src/main/res/values/strings.xml | 6 +- 10 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/com/example/android/eggtimernotifications/important/ImportantActivity.kt rename app/src/main/java/com/example/android/eggtimernotifications/{ui => timer}/EggTimerFragment.kt (97%) rename app/src/main/java/com/example/android/eggtimernotifications/{ui => timer}/EggTimerViewModel.kt (99%) create mode 100644 app/src/main/res/layout/important_activity.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 99db6fd0..1db1a3e7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,22 +1,9 @@ - - + package="com.example.android.eggtimernotifications" + > + + - + android:theme="@style/AppTheme" + > - + - + + - + android:name=".receiver.AlarmReceiver" + android:enabled="true" + android:exported="false" + /> - - + android:name=".receiver.SnoozeReceiver" + android:enabled="true" + android:exported="false" + /> \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/MainActivity.kt b/app/src/main/java/com/example/android/eggtimernotifications/MainActivity.kt index 10621e1d..70e943ba 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/MainActivity.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/MainActivity.kt @@ -18,7 +18,7 @@ package com.example.android.eggtimernotifications import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.example.android.eggtimernotifications.ui.EggTimerFragment +import com.example.android.eggtimernotifications.timer.EggTimerFragment class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/com/example/android/eggtimernotifications/important/ImportantActivity.kt b/app/src/main/java/com/example/android/eggtimernotifications/important/ImportantActivity.kt new file mode 100644 index 00000000..d313a072 --- /dev/null +++ b/app/src/main/java/com/example/android/eggtimernotifications/important/ImportantActivity.kt @@ -0,0 +1,14 @@ +package com.example.android.eggtimernotifications.important + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil.setContentView +import com.example.android.eggtimernotifications.R +import com.example.android.eggtimernotifications.databinding.ImportantActivityBinding + +class ImportantActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(this, R.layout.important_activity) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt b/app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerFragment.kt similarity index 97% rename from app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt rename to app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerFragment.kt index 73a2b9ff..26212df0 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerFragment.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerFragment.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.eggtimernotifications.ui +package com.example.android.eggtimernotifications.timer import android.os.Bundle import android.view.LayoutInflater diff --git a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt b/app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerViewModel.kt similarity index 99% rename from app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt rename to app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerViewModel.kt index e8270535..c2d83a12 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/ui/EggTimerViewModel.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/timer/EggTimerViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.eggtimernotifications.ui +package com.example.android.eggtimernotifications.timer import android.app.AlarmManager import android.app.Application diff --git a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt index f4056cb2..1448b2ea 100644 --- a/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/com/example/android/eggtimernotifications/util/NotificationExt.kt @@ -28,6 +28,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R +import com.example.android.eggtimernotifications.important.ImportantActivity import com.example.android.eggtimernotifications.receiver.SnoozeReceiver private val NOTIFICATION_ID = 0 @@ -43,9 +44,7 @@ fun NotificationManager.sendNotification( context: Context, ) { val contentIntent = Intent(context, MainActivity::class.java) - .apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } + .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val contentPendingIntent = PendingIntent.getActivity( context, REQUEST_CODE, @@ -70,6 +69,15 @@ fun NotificationManager.sendNotification( .bigPicture(eggImage) .bigLargeIcon(null) + val fullScreenIntent = Intent(context, ImportantActivity::class.java) + .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } + val fullScreenPedingIntent = PendingIntent.getActivity( + context, + REQUEST_CODE, + fullScreenIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + NotificationCompat.Builder( context, context.getString(R.string.egg_notification_channel_id) @@ -99,6 +107,8 @@ fun NotificationManager.sendNotification( // This information about your notification category is used by the system to make decisions about displaying your notification when the device is in Do Not Disturb mode. // https://developer.android.com/training/notify-user/build-notification#system-category .setCategory(NotificationCompat.CATEGORY_REMINDER) + .setFullScreenIntent(fullScreenPedingIntent, true) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .run { notify(NOTIFICATION_ID, this.build()) } diff --git a/app/src/main/res/layout/fragment_egg_timer.xml b/app/src/main/res/layout/fragment_egg_timer.xml index b5eab981..a6b7f7ac 100644 --- a/app/src/main/res/layout/fragment_egg_timer.xml +++ b/app/src/main/res/layout/fragment_egg_timer.xml @@ -23,13 +23,13 @@ + type="com.example.android.eggtimernotifications.timer.EggTimerViewModel" /> + tools:context=".timer.EggTimerFragment"> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..0aff7969 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 32dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 79164f9e..b09b51ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - - + + + + + \ No newline at end of file diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/EggTimerApplication.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/EggTimerApplication.kt new file mode 100644 index 00000000..e4a4ff2b --- /dev/null +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/EggTimerApplication.kt @@ -0,0 +1,11 @@ +package dev.filipebezerra.android.eggtimernotifications + +import android.app.Application +import timber.log.Timber + +class EggTimerApplication : Application() { + override fun onCreate() { + super.onCreate() + BuildConfig.DEBUG.takeIf { it }?.run { Timber.plant(Timber.DebugTree()) } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt new file mode 100644 index 00000000..9c5aaa83 --- /dev/null +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt @@ -0,0 +1,10 @@ +package dev.filipebezerra.android.eggtimernotifications.service + +import com.google.firebase.messaging.FirebaseMessagingService +import timber.log.Timber + +class EggTimerFirebaseMessagingService : FirebaseMessagingService() { + override fun onNewToken(newToken: String) { + Timber.i("Refreshed Firebase Token: $newToken") + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt index 8f6af3e5..0afa65b2 100644 --- a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt @@ -16,6 +16,7 @@ package dev.filipebezerra.android.eggtimernotifications.timer +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -45,14 +46,26 @@ class EggTimerFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - context?.run{ - getNotificationManager().createChannel( - getString(R.string.egg_notification_channel_id), - getString(R.string.egg_notification_channel_name) - ) + context?.run { + createEggTimerNotificationChannel() + createBreakfastNotificationChannel() } } + private fun Context.createEggTimerNotificationChannel() = + getNotificationManager().createChannel( + getString(R.string.egg_notification_channel_id), + getString(R.string.egg_notification_channel_name), + getString(R.string.egg_notification_channel_description) + ) + + private fun Context.createBreakfastNotificationChannel() = + getNotificationManager().createChannel( + getString(R.string.breakfast_notification_channel_id), + getString(R.string.breakfast_notification_channel_name), + getString(R.string.breakfast_notification_channel_description) + ) + companion object { fun newInstance() = EggTimerFragment() } diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt index 3998e0c8..548ecd6f 100644 --- a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt @@ -118,6 +118,7 @@ fun NotificationManager.sendNotification( fun NotificationManager.createChannel( channelId: String, channelName: String, + channelDescription: String, ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel( @@ -128,7 +129,7 @@ fun NotificationManager.createChannel( enableLights(true) lightColor = Color.RED enableVibration(true) - description = "Time for breakfast" + description = channelDescription setShowBadge(false) }.run { createNotificationChannel(this) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b09b51ac..01d424b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,10 +24,16 @@ FCM Message Failed to subscribe to topic Subscribed to topic + + egg_channel Egg + Time for breakfast + + fcm_default_channel Breakfast + It is time! Bon appetit! Egg timer is running… Breakfast reminder diff --git a/build.gradle b/build.gradle index 49bd34e1..64d9d446 100644 --- a/build.gradle +++ b/build.gradle @@ -25,8 +25,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.google.gms:google-services:4.3.4' } } @@ -34,7 +33,6 @@ allprojects { repositories { google() jcenter() - } } @@ -44,4 +42,7 @@ task clean(type: Delete) { ext { lifecycle_version = "2.2.0" + work_version = "2.4.0" + arch_version = "2.1.0" + fragment_version = "1.2.5" } \ No newline at end of file From 8e3b2c568f92355493e11982560f094384cd5083 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Fri, 1 Jan 2021 00:59:28 -0300 Subject: [PATCH 15/15] feat: Subscribe to breakfast topic and show notification from FCM message --- .../EggTimerFirebaseMessagingService.kt | 16 ++++++++++++++++ .../timer/EggTimerFragment.kt | 6 ++++++ .../util/FirebaseMessagingExt.kt | 19 +++++++++++++++++++ .../util/NotificationExt.kt | 2 ++ 4 files changed, 43 insertions(+) create mode 100644 app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/FirebaseMessagingExt.kt diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt index 9c5aaa83..a4892adc 100644 --- a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/service/EggTimerFirebaseMessagingService.kt @@ -1,10 +1,26 @@ package dev.filipebezerra.android.eggtimernotifications.service import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import dev.filipebezerra.android.eggtimernotifications.util.getNotificationManager +import dev.filipebezerra.android.eggtimernotifications.util.sendNotification import timber.log.Timber class EggTimerFirebaseMessagingService : FirebaseMessagingService() { override fun onNewToken(newToken: String) { Timber.i("Refreshed Firebase Token: $newToken") } + + override fun onMessageReceived(remoteMessage: RemoteMessage) { + with(remoteMessage) { + from?.run { Timber.d("Message received from $this") } + data.takeIf { it.isNotEmpty() }?.run { + Timber.d("Data received within the message $data") + } + notification?.body?.run { applicationContext.getNotificationManager().sendNotification( + this, + applicationContext + ) } + } + } } \ No newline at end of file diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt index 0afa65b2..46c18d1e 100644 --- a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/timer/EggTimerFragment.kt @@ -23,10 +23,12 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import com.google.firebase.messaging.FirebaseMessaging import dev.filipebezerra.android.eggtimernotifications.R import dev.filipebezerra.android.eggtimernotifications.databinding.FragmentEggTimerBinding import dev.filipebezerra.android.eggtimernotifications.util.createChannel import dev.filipebezerra.android.eggtimernotifications.util.getNotificationManager +import dev.filipebezerra.android.eggtimernotifications.util.subscribeToTopicBreakfast class EggTimerFragment : Fragment() { @@ -49,6 +51,7 @@ class EggTimerFragment : Fragment() { context?.run { createEggTimerNotificationChannel() createBreakfastNotificationChannel() + subscribeToTopicBreakfast() } } @@ -66,6 +69,9 @@ class EggTimerFragment : Fragment() { getString(R.string.breakfast_notification_channel_description) ) + private fun Context.subscribeToTopicBreakfast() = + FirebaseMessaging.getInstance().subscribeToTopicBreakfast(this) + companion object { fun newInstance() = EggTimerFragment() } diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/FirebaseMessagingExt.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/FirebaseMessagingExt.kt new file mode 100644 index 00000000..a7472c2a --- /dev/null +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/FirebaseMessagingExt.kt @@ -0,0 +1,19 @@ +package dev.filipebezerra.android.eggtimernotifications.util + +import android.content.Context +import android.widget.Toast +import com.google.firebase.messaging.FirebaseMessaging +import dev.filipebezerra.android.eggtimernotifications.R + +private const val BREAKFAST_TOPIC = "breakfast" + +fun FirebaseMessaging.subscribeToTopicBreakfast(context: Context) { + subscribeToTopic(BREAKFAST_TOPIC) + .addOnCompleteListener { + val userFeedback = if (it.isSuccessful) + R.string.message_subscribed + else + R.string.message_subscribe_failed + Toast.makeText(context.applicationContext, userFeedback, Toast.LENGTH_SHORT).show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt index 548ecd6f..90e1773c 100644 --- a/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt +++ b/app/src/main/java/dev/filipebezerra/android/eggtimernotifications/util/NotificationExt.kt @@ -26,6 +26,7 @@ import android.graphics.Color import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import dev.filipebezerra.android.eggtimernotifications.MainActivity import dev.filipebezerra.android.eggtimernotifications.R import dev.filipebezerra.android.eggtimernotifications.important.ImportantActivity @@ -110,6 +111,7 @@ fun NotificationManager.sendNotification( // https://developer.android.com/training/notify-user/build-notification#urgent-message .setFullScreenIntent(fullScreenPedingIntent, true) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVibrate(longArrayOf(100, 200, 100, 200)) .run { notify(NOTIFICATION_ID, this.build()) }