Skip to content

Commit

Permalink
Merge pull request #35 from avidraghav/improved_error_handling
Browse files Browse the repository at this point in the history
Improve error handling in the set Reminder flow
  • Loading branch information
avidraghav authored Mar 23, 2024
2 parents 0d3dc49 + abe5aab commit 5e405a5
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import com.raghav.spacedawnv2.domain.model.LaunchDetail
import com.raghav.spacedawnv2.domain.model.toReminder
import com.raghav.spacedawnv2.domain.repository.LaunchesRepository
import com.raghav.spacedawnv2.domain.usecase.AddReminderUseCase
import com.raghav.spacedawnv2.domain.util.Constants
import com.raghav.spacedawnv2.domain.util.NotificationPermissionException
import com.raghav.spacedawnv2.domain.util.ReminderAndNotificationPermissionException
import com.raghav.spacedawnv2.domain.util.ReminderPermissionException
import com.raghav.spacedawnv2.domain.util.Resource
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
Expand Down Expand Up @@ -76,30 +78,36 @@ class LaunchesScreenVM @Inject constructor(
addReminderUseCase(reminder).let { result ->
when (result) {
is Resource.Error -> {
when (result.errorMessage) {
Constants.REMINDER_PERMISSION_NOT_AVAILABLE -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionToSetReminderNotGranted
)
}
if (result.exception != null) {
when (result.exception) {
is ReminderPermissionException -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionToSetReminderNotGranted
)
}

Constants.NOTIFICATION_PERMISSION_NOT_AVAILABLE -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionToSendNotificationsNotGranted
)
}
is NotificationPermissionException -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionToSendNotificationsNotGranted
)
}

Constants.NOTIFICATION_REMINDER_PERMISSION_NOT_AVAILABLE -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionsToSendNotificationsAndSetReminderNotGranted
)
}
is ReminderAndNotificationPermissionException -> {
_eventFlow.emit(
LaunchesScreenEvent.PermissionsToSendNotificationsAndSetReminderNotGranted
)
}

else -> {
_eventFlow.emit(
LaunchesScreenEvent.ReminderNotSet(result.errorMessage)
)
else -> {
_eventFlow.emit(
LaunchesScreenEvent.ReminderNotSet(result.errorMessage)
)
}
}
} else {
_eventFlow.emit(
LaunchesScreenEvent.ReminderNotSet(result.errorMessage)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.raghav.spacedawnv2.navigation

import android.app.Activity
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.raghav.spacedawnv2.R
import com.raghav.spacedawnv2.launchesscreen.LaunchesScreen
import com.raghav.spacedawnv2.remindersscreen.RemindersScreen
import com.raghav.spacedawnv2.util.Helpers.Companion.navigateSingleTopTo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Composable
fun SpaceDawnNavHost(
navController: NavHostController,
modifier: Modifier = Modifier,
snackbarHostState: SnackbarHostState,
coroutineScope: CoroutineScope
) {
NavHost(
navController = navController,
startDestination = LaunchesScreen.route,
modifier = modifier
) {
// Launches Screen
composable(
LaunchesScreen.route,
enterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Right)
},
exitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Left)
}
) {
val activity = (LocalContext.current as? Activity)
val actionLabel by rememberUpdatedState(
newValue = stringResource(id = R.string.reminders)
)
LaunchesScreen(
systemBackButtonClicked = { activity?.finish() },
reminderSetSuccessfully = {
coroutineScope.launch {
val actionTaken = snackbarHostState.showSnackbar(
it,
actionLabel = actionLabel,
withDismissAction = true,
duration = SnackbarDuration.Short
)
when (actionTaken) {
SnackbarResult.ActionPerformed -> {
navController.navigateSingleTopTo(RemindersScreen.route)
}

else -> {}
}
}
},
reminderNotSet = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
it,
withDismissAction = true
)
}
}
)
}
// Reminders Screen
composable(
RemindersScreen.route,
enterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Left)
},
exitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Right)
}
) {
val snackBarMessage by rememberUpdatedState(
newValue = stringResource(R.string.reminder_cancelled_successfully)
)
RemindersScreen(
onBackPressed = { navController.navigateSingleTopTo(LaunchesScreen.route) },
reminderNotCancelled = { message ->
coroutineScope.launch {
snackbarHostState.showSnackbar(
message.toString(),
withDismissAction = true
)
}
},
reminderCancelled = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
// need to extract to string res
snackBarMessage,
withDismissAction = true
)
}
}
)
}
}
}
101 changes: 6 additions & 95 deletions app/src/main/java/com/raghav/spacedawnv2/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.raghav.spacedawnv2.ui

import android.app.Activity
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
Expand All @@ -13,40 +11,30 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.raghav.spacedawnv2.R
import com.raghav.spacedawnv2.launchesscreen.LaunchesScreen
import com.raghav.spacedawnv2.navigation.Destination
import com.raghav.spacedawnv2.navigation.LaunchesScreen
import com.raghav.spacedawnv2.navigation.RemindersScreen
import com.raghav.spacedawnv2.remindersscreen.RemindersScreen
import com.raghav.spacedawnv2.navigation.SpaceDawnNavHost
import com.raghav.spacedawnv2.ui.theme.SpaceDawnTheme
import com.raghav.spacedawnv2.ui.theme.spacing
import com.raghav.spacedawnv2.util.Helpers.Companion.navigateSingleTopTo
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
Expand Down Expand Up @@ -81,89 +69,12 @@ fun SpaceDawnApp(modifier: Modifier = Modifier) {
SnackbarHost(snackbarHostState)
}
) { innerPadding ->
NavHost(
SpaceDawnNavHost(
navController = navController,
startDestination = LaunchesScreen.route,
modifier = Modifier.padding(innerPadding)
) {
// Launches Screen
composable(
LaunchesScreen.route,
enterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Right)
},
exitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Left)
}
) {
val activity = (LocalContext.current as? Activity)
val actionLabel by rememberUpdatedState(
newValue = stringResource(id = R.string.reminders)
)
LaunchesScreen(
systemBackButtonClicked = { activity?.finish() },
reminderSetSuccessfully = {
scope.launch {
val actionTaken = snackbarHostState.showSnackbar(
it,
actionLabel = actionLabel,
withDismissAction = true,
duration = SnackbarDuration.Short
)
when (actionTaken) {
SnackbarResult.ActionPerformed -> {
navController.navigateSingleTopTo(RemindersScreen.route)
}

else -> {}
}
}
},
reminderNotSet = {
scope.launch {
snackbarHostState.showSnackbar(
it,
withDismissAction = true
)
}
}
)
}
// Reminders Screen
composable(
RemindersScreen.route,
enterTransition = {
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Left)
},
exitTransition = {
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Right)
}
) {
val snackBarMessage by rememberUpdatedState(
newValue = stringResource(R.string.reminder_cancelled_successfully)
)
RemindersScreen(
onBackPressed = { navController.navigateSingleTopTo(LaunchesScreen.route) },
reminderNotCancelled = { message ->
scope.launch {
snackbarHostState.showSnackbar(
message.toString(),
withDismissAction = true
)
}
},
reminderCancelled = {
scope.launch {
snackbarHostState.showSnackbar(
// need to extract to string res
snackBarMessage,
withDismissAction = true
)
}
}
)
}
}
modifier = Modifier.padding(innerPadding),
snackbarHostState = snackbarHostState,
coroutineScope = scope
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.raghav.spacedawnv2.domain.usecase

import com.raghav.spacedawnv2.domain.model.Reminder
import com.raghav.spacedawnv2.domain.repository.LaunchesRepository
import com.raghav.spacedawnv2.domain.util.Constants
import com.raghav.spacedawnv2.domain.util.NotificationPermissionException
import com.raghav.spacedawnv2.domain.util.ReminderAndNotificationPermissionException
import com.raghav.spacedawnv2.domain.util.ReminderPermissionException
import com.raghav.spacedawnv2.domain.util.ReminderScheduler
import com.raghav.spacedawnv2.domain.util.ReminderState
import com.raghav.spacedawnv2.domain.util.Resource
Expand Down Expand Up @@ -49,23 +51,25 @@ class AddReminderUseCase @Inject constructor(
when {
reminderState.reminderPermission && !reminderState.notificationPermission -> {
Resource.Error(
message = Constants.NOTIFICATION_PERMISSION_NOT_AVAILABLE
exception = NotificationPermissionException()
)
}

!reminderState.reminderPermission && reminderState.notificationPermission -> {
Resource.Error(message = Constants.REMINDER_PERMISSION_NOT_AVAILABLE)
Resource.Error(
exception = ReminderPermissionException()
)
}

!reminderState.reminderPermission && !reminderState.notificationPermission -> {
Resource.Error(
message = Constants.NOTIFICATION_REMINDER_PERMISSION_NOT_AVAILABLE
exception = ReminderAndNotificationPermissionException()
)
}

else -> {
Resource.Error(
message = Constants.NOTIFICATION_REMINDER_PERMISSION_NOT_AVAILABLE
exception = ReminderAndNotificationPermissionException()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,10 @@ object Constants {
const val REMINDER_SET = "Reminder Set Successfully"
const val REMINDER_NOTIFICATION_CHANNEL = "Launch Reminders"
const val REMINDER_CHANNEL_ID = "launch_reminders"
const val REMINDER_PERMISSION_NOT_AVAILABLE = "reminder_permission_not_available"
const val NOTIFICATION_PERMISSION_NOT_AVAILABLE = "notification_permission_not_available"
const val NOTIFICATION_REMINDER_PERMISSION_NOT_AVAILABLE =
"notification_reminder_permission_not_available"
const val KEY_LAUNCH_NAME = "key_launch_name"
const val KEY_LAUNCH_ID = "key_reminder_name"
const val TEN_MINUTES_IN_MILLIS = 600_000L
const val REMINDER_SOUND_DURATION = 10_000L
const val REMINDER_NOTIFICATION_AND_CLEANUP = "reminder_notification_and_cleanup"
const val SET_REMINDER_AFTER_DEVICE_BOOT = "set_reminder_after_device_boot"
const val API_THROTTLED_MESSAGE = "Requests Limit Reached, please try after 1 hour"
const val UNKNOWN_ERROR_MESSAGE = "Some Unknown Error Occurred"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.raghav.spacedawnv2.domain.util

class ReminderPermissionException : Exception()
class NotificationPermissionException : Exception()
class ReminderAndNotificationPermissionException : Exception()
Loading

0 comments on commit 5e405a5

Please sign in to comment.