From 4a907002bd03012bc48720ac793ba6154ac2edfe Mon Sep 17 00:00:00 2001 From: omer358 Date: Tue, 6 Aug 2024 06:23:11 +0300 Subject: [PATCH 1/6] Improve Enums usage, ui enhancement --- .../rememberme/domain/model/RemindersRepetition.kt | 6 +++--- .../presentation/settings/SettingsScreen.kt | 12 ++---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/example/rememberme/domain/model/RemindersRepetition.kt b/app/src/main/java/com/example/rememberme/domain/model/RemindersRepetition.kt index 300abb7..9273af2 100644 --- a/app/src/main/java/com/example/rememberme/domain/model/RemindersRepetition.kt +++ b/app/src/main/java/com/example/rememberme/domain/model/RemindersRepetition.kt @@ -1,7 +1,7 @@ package com.example.rememberme.domain.model enum class RemindersRepetition { - OnceADay, - ThreeADay, - FiveADay + OnceADay { override fun toString(): String ="Once a day" }, + ThreeADay { override fun toString(): String = "Three a day" }, + FiveADay { override fun toString(): String = "Five a day" } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/example/rememberme/presentation/settings/SettingsScreen.kt index 8520188..1d0cdfd 100644 --- a/app/src/main/java/com/example/rememberme/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/example/rememberme/presentation/settings/SettingsScreen.kt @@ -169,19 +169,16 @@ fun RemindersRepetitionDialog( text = { Column { ReminderOptionRow( - "Once a day", RemindersRepetition.OnceADay, selectedOption, onRepetitionSelected ) ReminderOptionRow( - "Three a day", RemindersRepetition.ThreeADay, selectedOption, onRepetitionSelected ) ReminderOptionRow( - "Five a day", RemindersRepetition.FiveADay, selectedOption, onRepetitionSelected @@ -215,19 +212,16 @@ fun ThemeSelectionDialog( text = { Column { ThemeOptionRow( - "Light", ThemeMode.LIGHT, selectedOption, onThemeSelected ) ThemeOptionRow( - "Dark", ThemeMode.DARK, selectedOption, onThemeSelected ) ThemeOptionRow( - "System Default", ThemeMode.SYSTEM, selectedOption, onThemeSelected @@ -245,7 +239,6 @@ fun ThemeSelectionDialog( @Composable fun ThemeOptionRow( - label: String, themeMode: ThemeMode, selectedOption: ThemeMode, onOptionSelected: (ThemeMode) -> Unit @@ -262,13 +255,12 @@ fun ThemeOptionRow( onClick = { onOptionSelected(themeMode) } ) Spacer(modifier = Modifier.width(8.dp)) - Text(text = label) + Text(text = themeMode.toString()) } } @Composable fun ReminderOptionRow( - label: String, repetition: RemindersRepetition, selectedOption: RemindersRepetition, onOptionSelected: (RemindersRepetition) -> Unit @@ -284,7 +276,7 @@ fun ReminderOptionRow( onClick = { onOptionSelected(repetition) } ) Spacer(modifier = Modifier.width(8.dp)) - Text(text = label) + Text(text = repetition.toString()) } } From c9f852a5f250665deb59da19303f0eeff9c8f3df Mon Sep 17 00:00:00 2001 From: omer358 Date: Fri, 9 Aug 2024 22:33:19 +0300 Subject: [PATCH 2/6] remove log messages from PersonDetails.kt --- .../rememberme/presentation/details/PersonDetails.kt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt index 5f11038..80cace7 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -1,7 +1,5 @@ package com.example.rememberme.presentation.details - import android.content.res.Configuration.UI_MODE_NIGHT_YES -import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -69,14 +67,11 @@ fun PersonDetailsScreen( when { uiState.value.isLoading -> { LoadingStateScreen() - Log.d(TAG, "PersonDetailsScreen: Loading") } uiState.value.error != null -> { - Log.e(TAG, "PersonDetailsScreen: Error - ${uiState.value.error}") ErrorContent(uiState.value.error!!) } uiState.value.person != null -> { - Log.d(TAG, "PersonDetailsScreen: ${uiState.value.person}") PersonDetailsContent( uiState.value.person!!, navigateUp, @@ -85,7 +80,6 @@ fun PersonDetailsScreen( ) } else -> { - Log.e(TAG, "PersonDetailsScreen: Person not found") // Optionally, you can add a UI to show "Person not found" } } @@ -148,7 +142,6 @@ fun PersonDetailsContent( ) { IconButton( onClick = { - Log.d(TAG, "PersonDetailsContent: Back arrow Clicked!") navigateUp() }) { Icon( @@ -159,7 +152,6 @@ fun PersonDetailsContent( } Row { IconButton(onClick = { - Log.d(TAG, "PersonDetailsContent: Edit Person Clicked, navigating to edit screen with personId: ${person.id}") navigateToEditScreen(person.id) }) { Icon( @@ -169,7 +161,6 @@ fun PersonDetailsContent( ) } IconButton(onClick = { - Log.d(TAG, "PersonDetailsContent: Delete Person Clicked, Deleting person with personId: ${person.id}") onDeletePerson() navigateUp() }) { @@ -271,7 +262,6 @@ fun PersonDetailsContentPreview() { avatar = R.drawable.ic_m4 ), navigateUp = { - Log.d(TAG, "PersonDetailsContentPreview: Clicked") }, navigateToEditScreen = {}, onDeletePerson = {} From 91b3eeff96b8d450e8d661bc9c7a1b8a034021c0 Mon Sep 17 00:00:00 2001 From: omer358 Date: Fri, 9 Aug 2024 22:47:39 +0300 Subject: [PATCH 3/6] initialize notification services --- .../data/manager/NotificationService.kt | 77 +++++++++++++++++++ .../com/example/rememberme/di/AppModule.kt | 7 ++ 2 files changed, 84 insertions(+) create mode 100644 app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt diff --git a/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt b/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt new file mode 100644 index 0000000..67070b6 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt @@ -0,0 +1,77 @@ +package com.example.rememberme.data.manager + +import android.Manifest +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.example.rememberme.MainActivity +import com.example.rememberme.R +import com.example.rememberme.domain.model.People +import javax.inject.Inject + +class NotificationService @Inject constructor(private val context: Context) { + + private val CHANNEL_ID = "people_notification_channel" + private val NOTIFICATION_ID = 0 + + + init { + createNotificationChannel() + } + + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val name = "People Notification" + val descriptionText = "Reminders about the people you have met" + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { + description = descriptionText + } + val notificationManager: NotificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + fun showNotification(person: People) { + val intent = Intent(context, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + val pendingIntent: PendingIntent = + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_m2) + .setContentTitle("Do you Remember ${person.firstName}?") + .setContentText("You have met him at ${person.place}") + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setChannelId(CHANNEL_ID) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + + with(NotificationManagerCompat.from(context)) { + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return + } + notify(NOTIFICATION_ID, builder.build()) + } + } +} diff --git a/app/src/main/java/com/example/rememberme/di/AppModule.kt b/app/src/main/java/com/example/rememberme/di/AppModule.kt index 03516c6..11fd6aa 100644 --- a/app/src/main/java/com/example/rememberme/di/AppModule.kt +++ b/app/src/main/java/com/example/rememberme/di/AppModule.kt @@ -5,6 +5,7 @@ import android.content.Context import androidx.room.Room import com.example.rememberme.data.PeopleRepositoryImpl import com.example.rememberme.data.local.PeopleDatabase +import com.example.rememberme.data.manager.NotificationService import com.example.rememberme.data.manager.SettingsManagerImpl import com.example.rememberme.domain.manager.SettingsManager import com.example.rememberme.domain.repository.PeopleRepository @@ -121,4 +122,10 @@ object AppModule { setSchedule = SetSchedule(settingsManager) ) } + + @Provides + @Singleton + fun provideNotificationService(@ApplicationContext context: Context): NotificationService { + return NotificationService(context) + } } \ No newline at end of file From 474a86d4858b32f3a9cee63a864f7dbe616bd4ed Mon Sep 17 00:00:00 2001 From: omer358 Date: Sun, 11 Aug 2024 23:37:28 +0300 Subject: [PATCH 4/6] Impl work manager for reminders --- app/build.gradle.kts | 10 ++++ app/src/main/AndroidManifest.xml | 7 +++ .../com/example/rememberme/MainActivity.kt | 54 +++++++++++++++++++ .../rememberme/RememberMeApplication.kt | 15 +++++- .../data/manager/NotificationWorker.kt | 43 +++++++++++++++ .../domain/usecases/people/GetAllPeople.kt | 3 +- .../presentation/details/PersonDetails.kt | 25 +++++++-- .../details/PersonDetailsViewModel.kt | 8 ++- gradle/libs.versions.toml | 5 ++ 9 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/example/rememberme/data/manager/NotificationWorker.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1b5337a..35e9e77 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -105,6 +105,10 @@ dependencies { implementation(libs.hilt.android) implementation(libs.androidx.hilt.navigation.compose) kapt(libs.hilt.compiler) + // Hilt Worker + implementation(libs.androidx.hilt.work) + // When using Kotlin. + kapt(libs.androidx.hilt.compiler) // Unit Test testImplementation(libs.mockito.core) @@ -126,4 +130,10 @@ dependencies { // Data Store implementation(libs.androidx.datastore.preferences) + + // Work Manager + // Kotlin + coroutines + implementation(libs.androidx.work.runtime.ktx) + // optional - Test helpers + androidTestImplementation(libs.androidx.work.testing) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 76276a9..dc1f41f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/MainActivity.kt b/app/src/main/java/com/example/rememberme/MainActivity.kt index 625d7ba..5ee3be0 100644 --- a/app/src/main/java/com/example/rememberme/MainActivity.kt +++ b/app/src/main/java/com/example/rememberme/MainActivity.kt @@ -1,16 +1,39 @@ package com.example.rememberme +import android.Manifest.permission.POST_NOTIFICATIONS +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import com.example.rememberme.data.manager.NotificationWorker import dagger.hilt.android.AndroidEntryPoint +import java.util.concurrent.TimeUnit @AndroidEntryPoint class MainActivity : ComponentActivity() { // @Inject // lateinit var peopleDao: PeopleDao + + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + // Permission is granted. Schedule the notification work. + Log.i(TAG, "Permission granted") + scheduleNotificationWork() + } else { + Log.i(TAG, "Permission denied") + // Permission is denied. Handle the case. + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -24,5 +47,36 @@ class MainActivity : ComponentActivity() { setContent { RememberMeApp() } + + // Check for notification permission only if the SDK version is 33 or higher + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission( + this, + POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + // Permission is already granted + scheduleNotificationWork() + } else { + // Request permission + requestPermissionLauncher.launch(POST_NOTIFICATIONS) + } + } else { + // SDK version is lower than 33, no need to request POST_NOTIFICATIONS permission + scheduleNotificationWork() + } + } + private fun scheduleNotificationWork() { + // Schedule the notification + val notificationWorkRequest = + PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) + .addTag("notificationWork") + .setInitialDelay(5, TimeUnit.SECONDS) + .build() + WorkManager.getInstance(this).enqueue(notificationWorkRequest) } + companion object { + private const val TAG = "MainActivity" + } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/RememberMeApplication.kt b/app/src/main/java/com/example/rememberme/RememberMeApplication.kt index f402c70..78208fe 100644 --- a/app/src/main/java/com/example/rememberme/RememberMeApplication.kt +++ b/app/src/main/java/com/example/rememberme/RememberMeApplication.kt @@ -1,8 +1,21 @@ package com.example.rememberme import android.app.Application +import androidx.hilt.work.HiltWorkerFactory +import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp +import javax.inject.Inject @HiltAndroidApp -class RememberMeApplication: Application() { +class RememberMeApplication : Application(), Configuration.Provider { + + @Inject + lateinit var workerFactory: HiltWorkerFactory + + override val workManagerConfiguration: Configuration + get() = Configuration.Builder() + .setWorkerFactory(workerFactory) + .build() + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/data/manager/NotificationWorker.kt b/app/src/main/java/com/example/rememberme/data/manager/NotificationWorker.kt new file mode 100644 index 0000000..722545e --- /dev/null +++ b/app/src/main/java/com/example/rememberme/data/manager/NotificationWorker.kt @@ -0,0 +1,43 @@ +package com.example.rememberme.data.manager + + +import android.content.Context +import android.util.Log +import androidx.hilt.work.HiltWorker +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.example.rememberme.domain.usecases.people.GetAllPeople +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import kotlinx.coroutines.runBlocking +import kotlin.random.Random + +@HiltWorker +class NotificationWorker + @AssistedInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + private val getAllPeople: GetAllPeople, + private val notificationService: NotificationService + ) : Worker(context, workerParams) { + + override fun doWork(): Result { + Log.d(TAG, "Performing long running task in scheduled job") + return runBlocking { + getAllPeople().collect { people -> + if (people.isNotEmpty()) { + Log.d(TAG, "People: $people") + val randomPerson = people[Random.nextInt(people.size)] + notificationService.showNotification(randomPerson) + } else { + Log.d(TAG, "No people found") + } + } + Log.d(TAG, "Work finished") + Result.success() + } + } + companion object { + private const val TAG = "NotificationWorker" + } +} diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt index 8f3317e..1152fcf 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt @@ -3,8 +3,9 @@ package com.example.rememberme.domain.usecases.people import com.example.rememberme.domain.model.People import com.example.rememberme.domain.repository.PeopleRepository import kotlinx.coroutines.flow.Flow +import javax.inject.Inject -class GetAllPeople( +class GetAllPeople @Inject constructor( private val peopleRepository: PeopleRepository ) { operator fun invoke(): Flow> { diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt index 80cace7..687da7a 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -21,6 +21,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Card import androidx.compose.material3.Icon @@ -76,7 +77,8 @@ fun PersonDetailsScreen( uiState.value.person!!, navigateUp, navigateToEditScreen, - onDeletePerson = {viewModel.onEvent(PersonDetailsEvent.DeletePerson)} + onDeletePerson = {viewModel.onEvent(PersonDetailsEvent.DeletePerson)}, + onSendNotification = {viewModel.sendNotification()} ) } else -> { @@ -107,7 +109,8 @@ fun PersonDetailsContent( navigateUp: () -> Unit, navigateToEditScreen: (Long?) -> Unit, onDeletePerson: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onSendNotification: () -> Unit, ) { val scrollState = rememberScrollState() Column( @@ -151,9 +154,11 @@ fun PersonDetailsContent( ) } Row { - IconButton(onClick = { + IconButton( + onClick = { navigateToEditScreen(person.id) - }) { + } + ) { Icon( imageVector = Icons.Default.Edit, contentDescription = null, @@ -170,6 +175,15 @@ fun PersonDetailsContent( tint = MaterialTheme.colorScheme.onPrimary ) } + IconButton(onClick = { + onSendNotification() + }){ + Icon( + imageVector = Icons.Default.Notifications, + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimary + ) + } } } @@ -264,7 +278,8 @@ fun PersonDetailsContentPreview() { navigateUp = { }, navigateToEditScreen = {}, - onDeletePerson = {} + onDeletePerson = {}, + onSendNotification = {} ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt index 4557f36..92d88d6 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt @@ -3,6 +3,7 @@ package com.example.rememberme.presentation.details import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.rememberme.data.manager.NotificationService import com.example.rememberme.domain.usecases.people.PeopleUseCases import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -13,7 +14,8 @@ import javax.inject.Inject @HiltViewModel class PersonDetailsViewModel @Inject constructor( - private val peopleUseCases: PeopleUseCases + private val peopleUseCases: PeopleUseCases, + private val notificationManager: NotificationService ) : ViewModel() { private val _uiState = MutableStateFlow(PersonDetailsUiState()) val uiState: StateFlow = _uiState @@ -65,6 +67,10 @@ class PersonDetailsViewModel @Inject constructor( } } + fun sendNotification(){ + notificationManager.showNotification(_uiState.value.person!!) + } + companion object { private const val TAG = "PersonDetailsViewModel" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 128dfac..5d088ec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,7 @@ truth = "1.4.2" uiTextGoogleFonts = "1.6.8" junitKtx = "1.2.1" lifecycleRuntimeComposeAndroid = "2.8.4" +workRuntimeKtx = "2.9.1" @@ -40,6 +41,8 @@ androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTesting" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } +androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltNavigationCompose" } +androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltNavigationCompose" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-navigation-testing = { module = "androidx.navigation:navigation-testing", version.ref = "navigationCompose" } @@ -47,6 +50,8 @@ androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" } androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } +androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } +androidx-work-testing = { module = "androidx.work:work-testing", version.ref = "workRuntimeKtx" } core-testing = { module = "android.arch.core:core-testing", version.ref = "coreTestingVersion" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } From c712d2d57b9ed5129f4f3842bab1eb45533e0a4f Mon Sep 17 00:00:00 2001 From: omer358 Date: Mon, 12 Aug 2024 17:58:32 +0300 Subject: [PATCH 5/6] inject the usecases using hilt --- .../com/example/rememberme/MainActivity.kt | 22 +++++++++++++------ .../data/manager/NotificationService.kt | 7 ++++-- .../usecases/people/DeletePersonById.kt | 3 ++- .../domain/usecases/people/GetPersonById.kt | 3 ++- .../domain/usecases/people/InsertNewPerson.kt | 3 ++- .../domain/usecases/people/PeopleUseCases.kt | 4 +++- .../domain/usecases/people/UpdatePerson.kt | 3 ++- 7 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/example/rememberme/MainActivity.kt b/app/src/main/java/com/example/rememberme/MainActivity.kt index 5ee3be0..a020ff2 100644 --- a/app/src/main/java/com/example/rememberme/MainActivity.kt +++ b/app/src/main/java/com/example/rememberme/MainActivity.kt @@ -67,13 +67,21 @@ class MainActivity : ComponentActivity() { } } private fun scheduleNotificationWork() { - // Schedule the notification - val notificationWorkRequest = - PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) - .addTag("notificationWork") - .setInitialDelay(5, TimeUnit.SECONDS) - .build() - WorkManager.getInstance(this).enqueue(notificationWorkRequest) + Log.i(TAG, "Scheduling notification work") + val workManager = WorkManager.getInstance(this) + val existingWork = workManager.getWorkInfosByTag("notificationWork").get() + + if (existingWork.isEmpty()) { + Log.i(TAG, "No existing work found, scheduling new work") + val notificationWorkRequest = + PeriodicWorkRequestBuilder(1, TimeUnit.MINUTES) + .addTag("notificationWork") + .setInitialDelay(5, TimeUnit.SECONDS) + .build() + workManager.enqueue(notificationWorkRequest) + }else{ + Log.i(TAG, "Existing work found, not scheduling new work") + } } companion object { private const val TAG = "MainActivity" diff --git a/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt b/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt index 67070b6..b4d204a 100644 --- a/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt +++ b/app/src/main/java/com/example/rememberme/data/manager/NotificationService.kt @@ -14,6 +14,7 @@ import androidx.core.app.NotificationManagerCompat import com.example.rememberme.MainActivity import com.example.rememberme.R import com.example.rememberme.domain.model.People +import com.example.rememberme.presentation.navgraph.Routes import javax.inject.Inject class NotificationService @Inject constructor(private val context: Context) { @@ -30,7 +31,7 @@ class NotificationService @Inject constructor(private val context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = "People Notification" val descriptionText = "Reminders about the people you have met" - val importance = NotificationManager.IMPORTANCE_DEFAULT + val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText } @@ -43,6 +44,8 @@ class NotificationService @Inject constructor(private val context: Context) { fun showNotification(person: People) { val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra("destination", Routes.PersonDetailsScreen.route) + putExtra("personId", person.id) } val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) @@ -51,7 +54,7 @@ class NotificationService @Inject constructor(private val context: Context) { .setSmallIcon(R.drawable.ic_m2) .setContentTitle("Do you Remember ${person.firstName}?") .setContentText("You have met him at ${person.place}") - .setPriority(NotificationCompat.PRIORITY_HIGH) + .setPriority(NotificationCompat.PRIORITY_MAX) .setChannelId(CHANNEL_ID) .setContentIntent(pendingIntent) .setAutoCancel(true) diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/DeletePersonById.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/DeletePersonById.kt index ab93c85..1293994 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/DeletePersonById.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/DeletePersonById.kt @@ -4,8 +4,9 @@ import com.example.rememberme.domain.repository.PeopleRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class DeletePersonById( +class DeletePersonById @Inject constructor( private val peopleRepository: PeopleRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) { diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt index 03f9d19..08dbc67 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt @@ -3,8 +3,9 @@ package com.example.rememberme.domain.usecases.people import com.example.rememberme.domain.model.People import com.example.rememberme.domain.repository.PeopleRepository import kotlinx.coroutines.flow.Flow +import javax.inject.Inject -class GetPersonById( +class GetPersonById @Inject constructor( private val peopleRepository: PeopleRepository ) { suspend operator fun invoke(id: Long): Flow { diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/InsertNewPerson.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/InsertNewPerson.kt index 29d05a3..e0ddcca 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/InsertNewPerson.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/InsertNewPerson.kt @@ -5,8 +5,9 @@ import com.example.rememberme.domain.repository.PeopleRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class InsertNewPerson( +class InsertNewPerson @Inject constructor( private val peopleRepository: PeopleRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) { diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt index 466f9f2..1b93a33 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt @@ -1,6 +1,8 @@ package com.example.rememberme.domain.usecases.people - data class PeopleUseCases( + import javax.inject.Inject + + data class PeopleUseCases @Inject constructor( val getAllPeople: GetAllPeople, val getPersonById: GetPersonById, val insertPerson: InsertNewPerson, diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/UpdatePerson.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/UpdatePerson.kt index 21f74cc..c933fd7 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/UpdatePerson.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/UpdatePerson.kt @@ -6,8 +6,9 @@ import com.example.rememberme.domain.repository.PeopleRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class UpdatePerson( +class UpdatePerson @Inject constructor( private val peopleRepository: PeopleRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) { From 2c94936171503aeb8682474ac3d500f320e65222 Mon Sep 17 00:00:00 2001 From: omer358 Date: Tue, 13 Aug 2024 04:38:36 +0300 Subject: [PATCH 6/6] configure navigation deep link --- app/src/main/AndroidManifest.xml | 9 ++++++ .../data/manager/NotificationService.kt | 30 +++++++++---------- .../presentation/navgraph/NavGraph.kt | 4 ++- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dc1f41f..b3ea7f9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,6 +25,15 @@ + + + + + + val personId = it.arguments?.getString("personId")?.toLong() if (personId != null) {