Skip to content

Commit

Permalink
Merge pull request #40 from omer358/dev
Browse files Browse the repository at this point in the history
resolve #39
  • Loading branch information
omer358 authored Aug 13, 2024
2 parents 3bba7f5 + 2c94936 commit 0182dd2
Show file tree
Hide file tree
Showing 19 changed files with 283 additions and 37 deletions.
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
16 changes: 16 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:name=".RememberMeApplication"
android:allowBackup="true"
Expand All @@ -23,7 +25,21 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="people"
android:pathPattern="/.*"
android:scheme="app" />
</intent-filter>
</activity>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>

</manifest>
62 changes: 62 additions & 0 deletions app/src/main/java/com/example/rememberme/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -24,5 +47,44 @@ 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() {
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<NotificationWorker>(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"
}

}
Original file line number Diff line number Diff line change
@@ -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()


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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.net.Uri
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
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_HIGH
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 deepLinkIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("app://people/${person.id}")
).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

val pendingIntent: PendingIntent = PendingIntent.getActivity(
context,
0,
deepLinkIntent,
PendingIntent.FLAG_IMMUTABLE
)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_m2)
.setContentTitle("Do you remember ${person.firstName}?")
.setContentText("You met at ${person.place}")
.setPriority(NotificationCompat.PRIORITY_MAX)
.setChannelId(CHANNEL_ID)
.setContentIntent(pendingIntent)
.setAutoCancel(true)

with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}
notify(NOTIFICATION_ID, builder.build())
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/example/rememberme/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -121,4 +122,10 @@ object AppModule {
setSchedule = SetSchedule(settingsManager)
)
}

@Provides
@Singleton
fun provideNotificationService(@ApplicationContext context: Context): NotificationService {
return NotificationService(context)
}
}
Original file line number Diff line number Diff line change
@@ -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" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<People>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<People?> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
Expand Down
Loading

0 comments on commit 0182dd2

Please sign in to comment.