diff --git a/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt b/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt index 069b549dc..848ac325c 100644 --- a/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt +++ b/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt @@ -1,56 +1,133 @@ package com.vitorpamplona.amethyst.ui.components import android.util.Log +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.halilibo.richtext.markdown.Markdown +import com.halilibo.richtext.ui.RichTextStyle +import com.halilibo.richtext.ui.material3.Material3RichText +import com.halilibo.richtext.ui.resolveDefaults import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.CheckifItNeedsToRequestNotificationPermission import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +@OptIn(ExperimentalPermissionsApi::class) @Composable fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) { - if (!sharedPreferencesViewModel.sharedPrefs.dontShowPushNotificationSelector) { - val context = LocalContext.current - var distributorPresent by remember { - mutableStateOf(PushDistributorHandler.savedDistributorExists()) - } - if (!distributorPresent) { - LoadDistributors() { currentDistributor, list, readableListWithExplainer -> - if (!readableListWithExplainer.isEmpty()) { - SpinnerSelectionDialog( - title = stringResource(id = R.string.select_push_server), - options = readableListWithExplainer, - onSelect = { index -> - if (list[index] == "None") { - PushDistributorHandler.forceRemoveDistributor(context) - sharedPreferencesViewModel.dontShowPushNotificationSelector() - } else { - val fullDistributorName = list[index] - PushDistributorHandler.saveDistributor(fullDistributorName) - } - distributorPresent = true - Log.d("Amethyst", "NotificationScreen: Distributor registered.") - }, - onDismiss = { - distributorPresent = true - Log.d("Amethyst", "NotificationScreen: Distributor dialog dismissed.") - } - ) - } + val notificationPermissionState = CheckifItNeedsToRequestNotificationPermission(sharedPreferencesViewModel) + + if (notificationPermissionState.status.isGranted) { + if (!sharedPreferencesViewModel.sharedPrefs.dontShowPushNotificationSelector) { + val context = LocalContext.current + var distributorPresent by remember { + mutableStateOf(PushDistributorHandler.savedDistributorExists()) + } + if (!distributorPresent) { + LoadDistributors() { currentDistributor, list, readableListWithExplainer -> + if (readableListWithExplainer.size > 1) { + SpinnerSelectionDialog( + title = stringResource(id = R.string.select_push_server), + options = readableListWithExplainer, + onSelect = { index -> + if (list[index] == "None") { + PushDistributorHandler.forceRemoveDistributor(context) + sharedPreferencesViewModel.dontAskForNotificationPermissions() + sharedPreferencesViewModel.dontShowPushNotificationSelector() + } else { + val fullDistributorName = list[index] + PushDistributorHandler.saveDistributor(fullDistributorName) + } + distributorPresent = true + Log.d("Amethyst", "NotificationScreen: Distributor registered.") + }, + onDismiss = { + distributorPresent = true + Log.d("Amethyst", "NotificationScreen: Distributor dialog dismissed.") + } + ) + } else { + AlertDialog( + onDismissRequest = { + distributorPresent = true + }, + title = { + Text(stringResource(R.string.push_server_insteall_app)) + }, + text = { + Material3RichText( + style = RichTextStyle().resolveDefaults() + ) { + Markdown( + content = stringResource(R.string.push_server_insteall_app_description) + ) + } + }, + confirmButton = { + Row( + modifier = Modifier + .padding(all = 8.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextButton(onClick = { + distributorPresent = true + sharedPreferencesViewModel.dontShowPushNotificationSelector() + }) { + Text(stringResource(R.string.quick_action_dont_show_again_button)) + } + Button(onClick = { + distributorPresent = true + }, contentPadding = PaddingValues(horizontal = 16.dp)) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null + ) + Spacer(Modifier.width(8.dp)) + Text(stringResource(R.string.error_dialog_button_ok)) + } + } + } + } + ) + } + } + } else { + val currentDistributor = PushDistributorHandler.getSavedDistributor() + PushDistributorHandler.saveDistributor(currentDistributor) } - } else { - val currentDistributor = PushDistributorHandler.getSavedDistributor() - PushDistributorHandler.saveDistributor(currentDistributor) } } } @@ -100,6 +177,7 @@ fun PushNotificationSettingsRow(sharedPreferencesViewModel: SharedPreferencesVie selectedIndex = list.indexOf(currentDistributor) ) { index -> if (list[index] == "None") { + sharedPreferencesViewModel.dontAskForNotificationPermissions() sharedPreferencesViewModel.dontShowPushNotificationSelector() PushDistributorHandler.forceRemoveDistributor(context) } else { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index 7533e3e05..7e4269314 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -371,6 +371,7 @@ object LocalPreferences { automaticallyShowUrlPreview, automaticallyHideNavigationBars, automaticallyShowProfilePictures, + false, false ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt index b874ab270..881a9a965 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt @@ -12,7 +12,8 @@ data class Settings( val automaticallyShowUrlPreview: ConnectivityType = ConnectivityType.ALWAYS, val automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS, val automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.ALWAYS, - val dontShowPushNotificationSelector: Boolean = false + val dontShowPushNotificationSelector: Boolean = false, + val dontAskForNotificationPermissions: Boolean = false ) enum class ThemeType(val screenCode: Int, val resourceId: Int) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/SharedPreferencesViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/SharedPreferencesViewModel.kt index 6eb61fc0d..7ca3ccbce 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/SharedPreferencesViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/SharedPreferencesViewModel.kt @@ -31,6 +31,7 @@ class SettingsState() { var automaticallyHideNavigationBars by mutableStateOf(BooleanType.ALWAYS) var automaticallyShowProfilePictures by mutableStateOf(ConnectivityType.ALWAYS) var dontShowPushNotificationSelector by mutableStateOf(false) + var dontAskForNotificationPermissions by mutableStateOf(false) var isOnMobileData: State = mutableStateOf(false) @@ -87,6 +88,8 @@ class SharedPreferencesViewModel : ViewModel() { sharedPrefs.automaticallyShowUrlPreview = savedSettings.automaticallyShowUrlPreview sharedPrefs.automaticallyHideNavigationBars = savedSettings.automaticallyHideNavigationBars sharedPrefs.automaticallyShowProfilePictures = savedSettings.automaticallyShowProfilePictures + sharedPrefs.dontShowPushNotificationSelector = savedSettings.dontShowPushNotificationSelector + sharedPrefs.dontAskForNotificationPermissions = savedSettings.dontAskForNotificationPermissions updateLanguageInTheUI() } @@ -154,12 +157,19 @@ class SharedPreferencesViewModel : ViewModel() { } fun dontShowPushNotificationSelector() { - if (!sharedPrefs.dontShowPushNotificationSelector) { + if (sharedPrefs.dontShowPushNotificationSelector == false) { sharedPrefs.dontShowPushNotificationSelector = true saveSharedSettings() } } + fun dontAskForNotificationPermissions() { + if (sharedPrefs.dontAskForNotificationPermissions == false) { + sharedPrefs.dontAskForNotificationPermissions = true + saveSharedSettings() + } + } + fun updateConnectivityStatusState(isOnMobileDataState: State) { if (sharedPrefs.isOnMobileData != isOnMobileDataState) { sharedPrefs.isOnMobileData = isOnMobileDataState @@ -186,7 +196,8 @@ class SharedPreferencesViewModel : ViewModel() { sharedPrefs.automaticallyShowUrlPreview, sharedPrefs.automaticallyHideNavigationBars, sharedPrefs.automaticallyShowProfilePictures, - sharedPrefs.dontShowPushNotificationSelector + sharedPrefs.dontShowPushNotificationSelector, + sharedPrefs.dontAskForNotificationPermissions ) ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt index d48cf9aa4..fb83ae710 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt @@ -29,6 +29,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.patrykandpatrick.vico.compose.axis.axisLabelComponent @@ -79,8 +80,6 @@ fun NotificationScreen( WatchAccountForNotifications(notifFeedViewModel, accountViewModel) - CheckifItNeedsToRequestNotificationPermission() - val lifeCycleOwner = LocalLifecycleOwner.current DisposableEffect(lifeCycleOwner) { val observer = LifecycleEventObserver { _, event -> @@ -114,26 +113,29 @@ fun NotificationScreen( } } -// TODO: Turn this into an Account flag -var hasAlreadyAskedNotificationPermissions = false - @OptIn(ExperimentalPermissionsApi::class) @Composable -fun CheckifItNeedsToRequestNotificationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !hasAlreadyAskedNotificationPermissions) { - val notificationPermissionState = rememberPermissionState( - Manifest.permission.POST_NOTIFICATIONS - ) +fun CheckifItNeedsToRequestNotificationPermission( + sharedPreferencesViewModel: SharedPreferencesViewModel +): PermissionState { + val notificationPermissionState = rememberPermissionState( + Manifest.permission.POST_NOTIFICATIONS + ) - if (!notificationPermissionState.status.isGranted) { - hasAlreadyAskedNotificationPermissions = true + if (!sharedPreferencesViewModel.sharedPrefs.dontAskForNotificationPermissions) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (!notificationPermissionState.status.isGranted) { + sharedPreferencesViewModel.dontAskForNotificationPermissions() - // This will pause the APP, including the connection with relays. - LaunchedEffect(notificationPermissionState) { - notificationPermissionState.launchPermissionRequest() + // This will pause the APP, including the connection with relays. + LaunchedEffect(notificationPermissionState) { + notificationPermissionState.launchPermissionRequest() + } } } } + + return notificationPermissionState } @Composable diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81720bada..89cecdd8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -646,10 +646,16 @@ Read-only user No reactions setup - Select a Push Notification distributor + Select a UnifiedPush App Push Notification From installed UnifiedPush apps None Disables Push Notifications Uses app %1$s + + Push Notification Setup + + To receive push notifications, install any app that supports [Unified Push](https://unifiedpush.org/), such as [Nfty](https://ntfy.sh/). + After installing, select the app you want to use in the Settings. + diff --git a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt index 1b7525dc0..4409fc683 100644 --- a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt +++ b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/SelectNotificationProvider.kt @@ -1,10 +1,14 @@ package com.vitorpamplona.amethyst.ui.components import androidx.compose.runtime.Composable +import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.CheckifItNeedsToRequestNotificationPermission +@OptIn(ExperimentalPermissionsApi::class) @Composable fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) { + CheckifItNeedsToRequestNotificationPermission(sharedPreferencesViewModel) } @Composable