Adds an explainer to install UnifiedPush apps if they are not installed.
This commit is contained in:
+109
-31
@@ -1,56 +1,133 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.components
|
package com.vitorpamplona.amethyst.ui.components
|
||||||
|
|
||||||
import android.util.Log
|
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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
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.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
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.R
|
||||||
import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler
|
import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
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.SettingsRow
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
|
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
|
||||||
if (!sharedPreferencesViewModel.sharedPrefs.dontShowPushNotificationSelector) {
|
val notificationPermissionState = CheckifItNeedsToRequestNotificationPermission(sharedPreferencesViewModel)
|
||||||
val context = LocalContext.current
|
|
||||||
var distributorPresent by remember {
|
if (notificationPermissionState.status.isGranted) {
|
||||||
mutableStateOf(PushDistributorHandler.savedDistributorExists())
|
if (!sharedPreferencesViewModel.sharedPrefs.dontShowPushNotificationSelector) {
|
||||||
}
|
val context = LocalContext.current
|
||||||
if (!distributorPresent) {
|
var distributorPresent by remember {
|
||||||
LoadDistributors() { currentDistributor, list, readableListWithExplainer ->
|
mutableStateOf(PushDistributorHandler.savedDistributorExists())
|
||||||
if (!readableListWithExplainer.isEmpty()) {
|
}
|
||||||
SpinnerSelectionDialog(
|
if (!distributorPresent) {
|
||||||
title = stringResource(id = R.string.select_push_server),
|
LoadDistributors() { currentDistributor, list, readableListWithExplainer ->
|
||||||
options = readableListWithExplainer,
|
if (readableListWithExplainer.size > 1) {
|
||||||
onSelect = { index ->
|
SpinnerSelectionDialog(
|
||||||
if (list[index] == "None") {
|
title = stringResource(id = R.string.select_push_server),
|
||||||
PushDistributorHandler.forceRemoveDistributor(context)
|
options = readableListWithExplainer,
|
||||||
sharedPreferencesViewModel.dontShowPushNotificationSelector()
|
onSelect = { index ->
|
||||||
} else {
|
if (list[index] == "None") {
|
||||||
val fullDistributorName = list[index]
|
PushDistributorHandler.forceRemoveDistributor(context)
|
||||||
PushDistributorHandler.saveDistributor(fullDistributorName)
|
sharedPreferencesViewModel.dontAskForNotificationPermissions()
|
||||||
}
|
sharedPreferencesViewModel.dontShowPushNotificationSelector()
|
||||||
distributorPresent = true
|
} else {
|
||||||
Log.d("Amethyst", "NotificationScreen: Distributor registered.")
|
val fullDistributorName = list[index]
|
||||||
},
|
PushDistributorHandler.saveDistributor(fullDistributorName)
|
||||||
onDismiss = {
|
}
|
||||||
distributorPresent = true
|
distributorPresent = true
|
||||||
Log.d("Amethyst", "NotificationScreen: Distributor dialog dismissed.")
|
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)
|
selectedIndex = list.indexOf(currentDistributor)
|
||||||
) { index ->
|
) { index ->
|
||||||
if (list[index] == "None") {
|
if (list[index] == "None") {
|
||||||
|
sharedPreferencesViewModel.dontAskForNotificationPermissions()
|
||||||
sharedPreferencesViewModel.dontShowPushNotificationSelector()
|
sharedPreferencesViewModel.dontShowPushNotificationSelector()
|
||||||
PushDistributorHandler.forceRemoveDistributor(context)
|
PushDistributorHandler.forceRemoveDistributor(context)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ object LocalPreferences {
|
|||||||
automaticallyShowUrlPreview,
|
automaticallyShowUrlPreview,
|
||||||
automaticallyHideNavigationBars,
|
automaticallyHideNavigationBars,
|
||||||
automaticallyShowProfilePictures,
|
automaticallyShowProfilePictures,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ data class Settings(
|
|||||||
val automaticallyShowUrlPreview: ConnectivityType = ConnectivityType.ALWAYS,
|
val automaticallyShowUrlPreview: ConnectivityType = ConnectivityType.ALWAYS,
|
||||||
val automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS,
|
val automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS,
|
||||||
val automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.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) {
|
enum class ThemeType(val screenCode: Int, val resourceId: Int) {
|
||||||
|
|||||||
+13
-2
@@ -31,6 +31,7 @@ class SettingsState() {
|
|||||||
var automaticallyHideNavigationBars by mutableStateOf(BooleanType.ALWAYS)
|
var automaticallyHideNavigationBars by mutableStateOf(BooleanType.ALWAYS)
|
||||||
var automaticallyShowProfilePictures by mutableStateOf(ConnectivityType.ALWAYS)
|
var automaticallyShowProfilePictures by mutableStateOf(ConnectivityType.ALWAYS)
|
||||||
var dontShowPushNotificationSelector by mutableStateOf<Boolean>(false)
|
var dontShowPushNotificationSelector by mutableStateOf<Boolean>(false)
|
||||||
|
var dontAskForNotificationPermissions by mutableStateOf<Boolean>(false)
|
||||||
|
|
||||||
var isOnMobileData: State<Boolean> = mutableStateOf(false)
|
var isOnMobileData: State<Boolean> = mutableStateOf(false)
|
||||||
|
|
||||||
@@ -87,6 +88,8 @@ class SharedPreferencesViewModel : ViewModel() {
|
|||||||
sharedPrefs.automaticallyShowUrlPreview = savedSettings.automaticallyShowUrlPreview
|
sharedPrefs.automaticallyShowUrlPreview = savedSettings.automaticallyShowUrlPreview
|
||||||
sharedPrefs.automaticallyHideNavigationBars = savedSettings.automaticallyHideNavigationBars
|
sharedPrefs.automaticallyHideNavigationBars = savedSettings.automaticallyHideNavigationBars
|
||||||
sharedPrefs.automaticallyShowProfilePictures = savedSettings.automaticallyShowProfilePictures
|
sharedPrefs.automaticallyShowProfilePictures = savedSettings.automaticallyShowProfilePictures
|
||||||
|
sharedPrefs.dontShowPushNotificationSelector = savedSettings.dontShowPushNotificationSelector
|
||||||
|
sharedPrefs.dontAskForNotificationPermissions = savedSettings.dontAskForNotificationPermissions
|
||||||
|
|
||||||
updateLanguageInTheUI()
|
updateLanguageInTheUI()
|
||||||
}
|
}
|
||||||
@@ -154,12 +157,19 @@ class SharedPreferencesViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun dontShowPushNotificationSelector() {
|
fun dontShowPushNotificationSelector() {
|
||||||
if (!sharedPrefs.dontShowPushNotificationSelector) {
|
if (sharedPrefs.dontShowPushNotificationSelector == false) {
|
||||||
sharedPrefs.dontShowPushNotificationSelector = true
|
sharedPrefs.dontShowPushNotificationSelector = true
|
||||||
saveSharedSettings()
|
saveSharedSettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dontAskForNotificationPermissions() {
|
||||||
|
if (sharedPrefs.dontAskForNotificationPermissions == false) {
|
||||||
|
sharedPrefs.dontAskForNotificationPermissions = true
|
||||||
|
saveSharedSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateConnectivityStatusState(isOnMobileDataState: State<Boolean>) {
|
fun updateConnectivityStatusState(isOnMobileDataState: State<Boolean>) {
|
||||||
if (sharedPrefs.isOnMobileData != isOnMobileDataState) {
|
if (sharedPrefs.isOnMobileData != isOnMobileDataState) {
|
||||||
sharedPrefs.isOnMobileData = isOnMobileDataState
|
sharedPrefs.isOnMobileData = isOnMobileDataState
|
||||||
@@ -186,7 +196,8 @@ class SharedPreferencesViewModel : ViewModel() {
|
|||||||
sharedPrefs.automaticallyShowUrlPreview,
|
sharedPrefs.automaticallyShowUrlPreview,
|
||||||
sharedPrefs.automaticallyHideNavigationBars,
|
sharedPrefs.automaticallyHideNavigationBars,
|
||||||
sharedPrefs.automaticallyShowProfilePictures,
|
sharedPrefs.automaticallyShowProfilePictures,
|
||||||
sharedPrefs.dontShowPushNotificationSelector
|
sharedPrefs.dontShowPushNotificationSelector,
|
||||||
|
sharedPrefs.dontAskForNotificationPermissions
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-15
@@ -29,6 +29,7 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
|
import com.google.accompanist.permissions.PermissionState
|
||||||
import com.google.accompanist.permissions.isGranted
|
import com.google.accompanist.permissions.isGranted
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
import com.patrykandpatrick.vico.compose.axis.axisLabelComponent
|
import com.patrykandpatrick.vico.compose.axis.axisLabelComponent
|
||||||
@@ -79,8 +80,6 @@ fun NotificationScreen(
|
|||||||
|
|
||||||
WatchAccountForNotifications(notifFeedViewModel, accountViewModel)
|
WatchAccountForNotifications(notifFeedViewModel, accountViewModel)
|
||||||
|
|
||||||
CheckifItNeedsToRequestNotificationPermission()
|
|
||||||
|
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
DisposableEffect(lifeCycleOwner) {
|
DisposableEffect(lifeCycleOwner) {
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
@@ -114,26 +113,29 @@ fun NotificationScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Turn this into an Account flag
|
|
||||||
var hasAlreadyAskedNotificationPermissions = false
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckifItNeedsToRequestNotificationPermission() {
|
fun CheckifItNeedsToRequestNotificationPermission(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !hasAlreadyAskedNotificationPermissions) {
|
sharedPreferencesViewModel: SharedPreferencesViewModel
|
||||||
val notificationPermissionState = rememberPermissionState(
|
): PermissionState {
|
||||||
Manifest.permission.POST_NOTIFICATIONS
|
val notificationPermissionState = rememberPermissionState(
|
||||||
)
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
|
)
|
||||||
|
|
||||||
if (!notificationPermissionState.status.isGranted) {
|
if (!sharedPreferencesViewModel.sharedPrefs.dontAskForNotificationPermissions) {
|
||||||
hasAlreadyAskedNotificationPermissions = true
|
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.
|
// This will pause the APP, including the connection with relays.
|
||||||
LaunchedEffect(notificationPermissionState) {
|
LaunchedEffect(notificationPermissionState) {
|
||||||
notificationPermissionState.launchPermissionRequest()
|
notificationPermissionState.launchPermissionRequest()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return notificationPermissionState
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -646,10 +646,16 @@
|
|||||||
<string name="read_only_user">Read-only user</string>
|
<string name="read_only_user">Read-only user</string>
|
||||||
<string name="no_reactions_setup">No reactions setup</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_title">Push Notification</string>
|
||||||
<string name="push_server_explainer">From installed UnifiedPush apps</string>
|
<string name="push_server_explainer">From installed UnifiedPush apps</string>
|
||||||
<string name="push_server_none">None</string>
|
<string name="push_server_none">None</string>
|
||||||
<string name="push_server_none_explainer">Disables Push Notifications</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_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>
|
</resources>
|
||||||
|
|||||||
+4
@@ -1,10 +1,14 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.components
|
package com.vitorpamplona.amethyst.ui.components
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CheckifItNeedsToRequestNotificationPermission
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
|
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
|
||||||
|
CheckifItNeedsToRequestNotificationPermission(sharedPreferencesViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
Reference in New Issue
Block a user