Adds an explainer to install UnifiedPush apps if they are not installed.

This commit is contained in:
Vitor Pamplona
2023-10-27 13:05:47 -04:00
parent 56af44b00b
commit 6a8c756f7a
7 changed files with 153 additions and 50 deletions
@@ -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 {
@@ -371,6 +371,7 @@ object LocalPreferences {
automaticallyShowUrlPreview,
automaticallyHideNavigationBars,
automaticallyShowProfilePictures,
false,
false
)
}
@@ -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) {
@@ -31,6 +31,7 @@ class SettingsState() {
var automaticallyHideNavigationBars by mutableStateOf(BooleanType.ALWAYS)
var automaticallyShowProfilePictures by mutableStateOf(ConnectivityType.ALWAYS)
var dontShowPushNotificationSelector by mutableStateOf<Boolean>(false)
var dontAskForNotificationPermissions by mutableStateOf<Boolean>(false)
var isOnMobileData: State<Boolean> = 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<Boolean>) {
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
)
)
}
@@ -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
+7 -1
View File
@@ -646,10 +646,16 @@
<string name="read_only_user">Read-only user</string>
<string name="no_reactions_setup">No reactions setup</string>
<string name="select_push_server">Select a Push Notification distributor</string>
<string name="select_push_server">Select a UnifiedPush App</string>
<string name="push_server_title">Push Notification</string>
<string name="push_server_explainer">From installed UnifiedPush apps</string>
<string name="push_server_none">None</string>
<string name="push_server_none_explainer">Disables Push Notifications</string>
<string name="push_server_uses_app_explainer">Uses app %1$s</string>
<string name="push_server_insteall_app">Push Notification Setup</string>
<string name="push_server_insteall_app_description">
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.
</string>
</resources>
@@ -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