Skip to content

Commit

Permalink
Merge pull request #47 from omer358/dev
Browse files Browse the repository at this point in the history
resolves #46
  • Loading branch information
omer358 authored Aug 19, 2024
2 parents 718287b + 04ea850 commit 24b087b
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 80 deletions.
111 changes: 64 additions & 47 deletions app/src/main/java/com/example/rememberme/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
Expand All @@ -23,6 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit


@AndroidEntryPoint
class MainActivity : ComponentActivity() {

Expand All @@ -31,84 +33,99 @@ class MainActivity : ComponentActivity() {

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
) { isGranted ->
handlePermissionResult(isGranted)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupUI()
observeUiState()
}

private fun setupUI() {
enableEdgeToEdge()
installSplashScreen()
setContent { RememberMeApp() }
}

private fun observeUiState() {
lifecycleScope.launch {
settingsViewModel.uiState.collect { uiState ->
if (isGranted && uiState.remindersRepetition != lastRepetition) {
// Permission is granted. Schedule the notification work with the correct repetition.
Log.i(TAG, "Permission granted")
scheduleNotificationWork(uiState.remindersRepetition)
lastRepetition = uiState.remindersRepetition
} else {
Log.i(TAG, "Permission denied")
// Permission is denied. Handle the case.
if (shouldScheduleWork(uiState.remindersRepetition)) {
handlePermissionsAndScheduleWork(uiState.remindersRepetition)
}
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
installSplashScreen()
private fun shouldScheduleWork(currentRepetition: RemindersRepetition): Boolean {
return currentRepetition != lastRepetition
}

setContent {
RememberMeApp()
private fun handlePermissionsAndScheduleWork(repetition: RemindersRepetition) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (hasNotificationPermission()) {
scheduleNotificationWork(repetition)
} else {
requestNotificationPermission()
}
} else {
scheduleNotificationWork(repetition)
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun hasNotificationPermission(): Boolean {
return ContextCompat.checkSelfPermission(
this, POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun requestNotificationPermission() {
requestPermissionLauncher.launch(POST_NOTIFICATIONS)
}

private fun handlePermissionResult(isGranted: Boolean) {
lifecycleScope.launch {
settingsViewModel.uiState.collect { uiState ->
Log.i(TAG, "Settings UI state: $uiState")
val remindersRepetition = uiState.remindersRepetition

if (remindersRepetition != lastRepetition) {
// 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@MainActivity,
POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
// Permission is already granted
scheduleNotificationWork(remindersRepetition)
} else {
// Request permission
requestPermissionLauncher.launch(POST_NOTIFICATIONS)
}
} else {
// SDK version is lower than 33, no need to request POST_NOTIFICATIONS permission
scheduleNotificationWork(remindersRepetition)
}
lastRepetition = remindersRepetition
if (isGranted && shouldScheduleWork(uiState.remindersRepetition)) {
scheduleNotificationWork(uiState.remindersRepetition)
}
}
}
}

private fun scheduleNotificationWork(repetition: RemindersRepetition) {
Log.i(TAG, "Scheduling notification work with repetition: $repetition")
val repeatInterval = when (repetition) {
RemindersRepetition.OnceADay -> 24
RemindersRepetition.ThreeADay -> 24 / 3
RemindersRepetition.FiveADay -> 24 / 5
}
val repeatInterval = getRepeatIntervalInHours(repetition).toLong()

Log.i(TAG, "Scheduling notification work with interval: $repeatInterval hours")
val notificationWorkRequest = PeriodicWorkRequestBuilder<NotificationWorker>(
repeatInterval.toLong(),
TimeUnit.HOURS)
.addTag("notificationWorkRequest")
.setInitialDelay(repeatInterval.toLong(), TimeUnit.HOURS)
repeatInterval, TimeUnit.HOURS
).addTag("notificationWorkRequest")
.setInitialDelay(10, TimeUnit.SECONDS)
.build()

WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"notificationWork",
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
ExistingPeriodicWorkPolicy.UPDATE,
notificationWorkRequest
)
lastRepetition = repetition
}

private fun getRepeatIntervalInHours(repetition: RemindersRepetition): Int {
return when (repetition) {
RemindersRepetition.OnceADay -> 24
RemindersRepetition.ThreeADay -> 24 / 3
RemindersRepetition.FiveADay -> 24 / 5
}
}

companion object {
private const val TAG = "MainActivity"
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
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

companion object {
private const val CHANNEL_ID = "people_notification_channel"
private const 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 name = context.getString(R.string.notification_channel_name)
val descriptionText = context.getString(R.string.notification_channel_description)
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
Expand All @@ -41,6 +46,14 @@ class NotificationService @Inject constructor(private val context: Context) {
}

fun showNotification(person: People) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}

val deepLinkIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("app://people/${person.id}")
Expand All @@ -52,27 +65,39 @@ class NotificationService @Inject constructor(private val context: Context) {
context,
0,
deepLinkIntent,
PendingIntent.FLAG_IMMUTABLE
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
val drawable: Drawable? = ContextCompat.getDrawable(context, person.avatar)
val largeIcon: Bitmap? = drawable?.let { drawableToBitmap(it) }

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_m2)
.setContentTitle("Do you remember ${person.firstName}?")
.setContentText("You met at ${person.place}")
.setContentTitle(context.getString(R.string.notification_title, person.firstName))
.setContentText(context.getString(R.string.notification_text, person.place))
.setLargeIcon(largeIcon)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setChannelId(CHANNEL_ID)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_REMINDER)
.build()

with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}
notify(NOTIFICATION_ID, builder.build())
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}

private fun drawableToBitmap(drawable: Drawable): Bitmap {
return if (drawable is BitmapDrawable) {
drawable.bitmap
} else {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = android.graphics.Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.example.rememberme.presentation.navgraph

import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.EaseIn
import androidx.compose.animation.core.EaseOut
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
Expand All @@ -20,6 +19,7 @@ import com.example.rememberme.presentation.peopleList.PeopleScreen
import com.example.rememberme.presentation.settings.SettingsScreen

private const val TAG = "NavGraph"

@Composable
fun NavGraph(
startDestination: String,
Expand All @@ -29,15 +29,28 @@ fun NavGraph(
navController = navController,
startDestination = startDestination,
enterTransition = {
slideIntoContainer(
animationSpec = tween(300, easing = EaseIn),
towards = AnimatedContentTransitionScope.SlideDirection.Start
slideIntoContainer(
AnimatedContentTransitionScope.SlideDirection.Start,
animationSpec = tween(700, easing = FastOutSlowInEasing)

)
},
exitTransition = {
slideOutOfContainer(
animationSpec = tween(300, easing = EaseOut),
towards = AnimatedContentTransitionScope.SlideDirection.End
AnimatedContentTransitionScope.SlideDirection.Start,
animationSpec = tween(700)
)
},
popEnterTransition = {
slideIntoContainer(
AnimatedContentTransitionScope.SlideDirection.End,
animationSpec = tween(700)
)
},
popExitTransition = {
slideOutOfContainer(
AnimatedContentTransitionScope.SlideDirection.End,
animationSpec = tween(700)
)
}
) {
Expand Down Expand Up @@ -78,7 +91,9 @@ fun NavGraph(
}
composable(
route = "${Routes.AddPersonScreen.route}/{personId}",
arguments = listOf(navArgument("personId") { type = NavType.StringType; nullable = true })
arguments = listOf(navArgument("personId") {
type = NavType.StringType; nullable = true
})
) {
val personId = it.arguments?.getString("personId")?.toLongOrNull()

Expand All @@ -101,19 +116,19 @@ fun NavGraph(
navigateUp = {
navController.navigateUp()
},
navigateToEditScreen = {id ->
navigateToEditScreen = { id ->
navController.navigate("${Routes.AddPersonScreen.route}/$id")
}
)
}
}
composable(route = Routes.SettingsScreen.route) {
SettingsScreen(
popUp ={
popUp = {
navController.navigateUp()
}
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ fun ThemeOptionRow(
selected = (themeMode == selectedOption),
onClick = { onOptionSelected(themeMode) }
)
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.width(4.dp))
Text(text = themeMode.toString())
}
}
Expand All @@ -269,13 +269,14 @@ fun ReminderOptionRow(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.clickable { onOptionSelected(repetition) }
.clickable { onOptionSelected(repetition) },
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = (repetition == selectedOption),
onClick = { onOptionSelected(repetition) }
)
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.width(4.dp))
Text(text = repetition.toString())
}
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">تذكرني</string>
<string name="notification_channel_name">People Reminders</string>
<string name="notification_channel_description">Reminders about the people you have met</string>
</resources>
Loading

0 comments on commit 24b087b

Please sign in to comment.