mirror of
https://github.com/lone-cloud/prism-android
synced 2026-06-03 19:54:44 -07:00
improving copy, better prism registration, allow clearing prism registration
This commit is contained in:
parent
05607cb9f1
commit
e767e89ee9
18 changed files with 194 additions and 68 deletions
26
.github/copilot-instructions.md
vendored
Normal file
26
.github/copilot-instructions.md
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# GitHub Copilot Instructions
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Do NOT add obvious comments that just describe what the code does
|
||||||
|
- Only add comments for complex logic, non-obvious behavior, or important context
|
||||||
|
- Prefer self-documenting code with clear variable and function names over comments
|
||||||
|
- Avoid redundant comments like "// Create button" or "// Set text color"
|
||||||
|
|
||||||
|
## Examples of BAD comments to avoid:
|
||||||
|
```kotlin
|
||||||
|
// Description with clickable link
|
||||||
|
val description = stringResource(R.string.prism_server_description)
|
||||||
|
|
||||||
|
// Get the URI handler
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples of GOOD comments:
|
||||||
|
```kotlin
|
||||||
|
// Retry with exponential backoff, max 5 attempts
|
||||||
|
for (attempt in 1..5) { ... }
|
||||||
|
|
||||||
|
// WorkAround: Android 12+ requires explicit mutation flag
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_MUTABLE)
|
||||||
|
```
|
||||||
|
|
@ -6,4 +6,4 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
A notification provider to use with [Prism](https://github.com/lone-cloud/prism)
|
A notification provider for [Prism](https://github.com/lone-cloud/prism) and [UnifiedPush](https://unifiedpush.org/) applications.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import androidx.core.content.edit
|
||||||
import org.unifiedpush.android.distributor.MigrationManager
|
import org.unifiedpush.android.distributor.MigrationManager
|
||||||
import org.unifiedpush.android.distributor.Store
|
import org.unifiedpush.android.distributor.Store
|
||||||
|
|
||||||
class AppStore(context: Context) :
|
class PrismPreferences(context: Context) :
|
||||||
Store(context, PREF_NAME),
|
Store(context, PREF_NAME),
|
||||||
MigrationManager.MigrationStore {
|
MigrationManager.MigrationStore {
|
||||||
var uaid: String?
|
var uaid: String?
|
||||||
|
|
@ -3,7 +3,7 @@ package app.lonecloud.prism
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.DatabaseFactory
|
import app.lonecloud.prism.DatabaseFactory
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
@ -23,11 +23,7 @@ object PrismServerClient {
|
||||||
.readTimeout(10, TimeUnit.SECONDS)
|
.readTimeout(10, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private fun getAuthHeader(apiKey: String): String {
|
private fun getAuthHeader(apiKey: String): String = "Bearer $apiKey"
|
||||||
val credentials = ":$apiKey"
|
|
||||||
val encoded = Base64.encodeToString(credentials.toByteArray(), Base64.NO_WRAP)
|
|
||||||
return "Basic $encoded"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerApp(
|
fun registerApp(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|
@ -39,7 +35,7 @@ object PrismServerClient {
|
||||||
onSuccess: () -> Unit = {},
|
onSuccess: () -> Unit = {},
|
||||||
onError: (String) -> Unit = {}
|
onError: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
val serverUrl = store.prismServerUrl
|
val serverUrl = store.prismServerUrl
|
||||||
val apiKey = store.prismApiKey
|
val apiKey = store.prismApiKey
|
||||||
|
|
||||||
|
|
@ -86,7 +82,7 @@ object PrismServerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerAllApps(context: Context) {
|
fun registerAllApps(context: Context) {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
val serverUrl = store.prismServerUrl
|
val serverUrl = store.prismServerUrl
|
||||||
val apiKey = store.prismApiKey
|
val apiKey = store.prismApiKey
|
||||||
|
|
||||||
|
|
@ -128,7 +124,7 @@ object PrismServerClient {
|
||||||
onSuccess: () -> Unit = {},
|
onSuccess: () -> Unit = {},
|
||||||
onError: (String) -> Unit = {}
|
onError: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
val url = serverUrl ?: store.prismServerUrl
|
val url = serverUrl ?: store.prismServerUrl
|
||||||
val key = apiKey ?: store.prismApiKey
|
val key = apiKey ?: store.prismApiKey
|
||||||
|
|
||||||
|
|
@ -170,7 +166,7 @@ object PrismServerClient {
|
||||||
serverUrl: String? = null,
|
serverUrl: String? = null,
|
||||||
apiKey: String? = null
|
apiKey: String? = null
|
||||||
) {
|
) {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
val url = serverUrl ?: store.prismServerUrl
|
val url = serverUrl ?: store.prismServerUrl
|
||||||
val key = apiKey ?: store.prismApiKey
|
val key = apiKey ?: store.prismApiKey
|
||||||
|
|
||||||
|
|
@ -206,8 +202,9 @@ object PrismServerClient {
|
||||||
) {
|
) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
|
val healthUrl = "$serverUrl/api/health"
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(serverUrl)
|
.url(healthUrl)
|
||||||
.addHeader("Authorization", getAuthHeader(apiKey))
|
.addHeader("Authorization", getAuthHeader(apiKey))
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.DatabaseFactory
|
import app.lonecloud.prism.DatabaseFactory
|
||||||
import app.lonecloud.prism.EncryptionKeyStore
|
import app.lonecloud.prism.EncryptionKeyStore
|
||||||
import app.lonecloud.prism.PrismServerClient
|
import app.lonecloud.prism.PrismServerClient
|
||||||
import app.lonecloud.prism.activities.ui.InstalledApp
|
import app.lonecloud.prism.activities.ui.InstalledApp
|
||||||
|
|
@ -41,8 +41,8 @@ class MainViewModel(
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
constructor(requireBatteryOpt: Boolean, messenger: InternalMessenger?, application: Application) : this(
|
constructor(requireBatteryOpt: Boolean, messenger: InternalMessenger?, application: Application) : this(
|
||||||
mainUiState = MainUiState(
|
mainUiState = MainUiState(
|
||||||
prismServerConfigured = !AppStore(application).prismServerUrl.isNullOrBlank() &&
|
prismServerConfigured = !PrismPreferences(application).prismServerUrl.isNullOrBlank() &&
|
||||||
!AppStore(application).prismApiKey.isNullOrBlank()
|
!PrismPreferences(application).prismApiKey.isNullOrBlank()
|
||||||
),
|
),
|
||||||
batteryOptimisationViewModel = BatteryOptimisationViewModel(requireBatteryOpt, messenger),
|
batteryOptimisationViewModel = BatteryOptimisationViewModel(requireBatteryOpt, messenger),
|
||||||
registrationsViewModel = RegistrationsViewModel(
|
registrationsViewModel = RegistrationsViewModel(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.PrismServerClient
|
import app.lonecloud.prism.PrismServerClient
|
||||||
import app.lonecloud.prism.activities.ui.SettingsState
|
import app.lonecloud.prism.activities.ui.SettingsState
|
||||||
import app.lonecloud.prism.receivers.PrismConfigReceiver
|
import app.lonecloud.prism.receivers.PrismConfigReceiver
|
||||||
|
|
@ -33,7 +33,7 @@ class SettingsViewModel(
|
||||||
fun toggleShowToasts() {
|
fun toggleShowToasts() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
state = state.copy(showToasts = !state.showToasts)
|
state = state.copy(showToasts = !state.showToasts)
|
||||||
application?.let { AppStore(it).showToasts = state.showToasts }
|
application?.let { PrismPreferences(it).showToasts = state.showToasts }
|
||||||
messenger?.sendIMessage(InternalOpcode.SHOW_TOASTS_SET, if (state.showToasts) 1 else 0)
|
messenger?.sendIMessage(InternalOpcode.SHOW_TOASTS_SET, if (state.showToasts) 1 else 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ class SettingsViewModel(
|
||||||
val trimmedUrl = url.trim()
|
val trimmedUrl = url.trim()
|
||||||
state = state.copy(prismServerUrl = trimmedUrl)
|
state = state.copy(prismServerUrl = trimmedUrl)
|
||||||
application?.let {
|
application?.let {
|
||||||
AppStore(it).prismServerUrl = trimmedUrl.ifBlank { null }
|
PrismPreferences(it).prismServerUrl = trimmedUrl.ifBlank { null }
|
||||||
|
|
||||||
val intent = Intent(PrismConfigReceiver.ACTION_SET_PRISM_SERVER_URL).apply {
|
val intent = Intent(PrismConfigReceiver.ACTION_SET_PRISM_SERVER_URL).apply {
|
||||||
putExtra(PrismConfigReceiver.EXTRA_URL, trimmedUrl)
|
putExtra(PrismConfigReceiver.EXTRA_URL, trimmedUrl)
|
||||||
|
|
@ -65,7 +65,7 @@ class SettingsViewModel(
|
||||||
val trimmedKey = apiKey.trim()
|
val trimmedKey = apiKey.trim()
|
||||||
state = state.copy(prismApiKey = trimmedKey)
|
state = state.copy(prismApiKey = trimmedKey)
|
||||||
application?.let {
|
application?.let {
|
||||||
AppStore(it).prismApiKey = trimmedKey.ifBlank { null }
|
PrismPreferences(it).prismApiKey = trimmedKey.ifBlank { null }
|
||||||
|
|
||||||
val intent = Intent(PrismConfigReceiver.ACTION_SET_PRISM_API_KEY).apply {
|
val intent = Intent(PrismConfigReceiver.ACTION_SET_PRISM_API_KEY).apply {
|
||||||
putExtra(PrismConfigReceiver.EXTRA_API_KEY, trimmedKey)
|
putExtra(PrismConfigReceiver.EXTRA_API_KEY, trimmedKey)
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.unifiedpush.android.distributor.ipc.InternalMessenger
|
import org.unifiedpush.android.distributor.ipc.InternalMessenger
|
||||||
import org.unifiedpush.android.distributor.ipc.InternalOpcode
|
import org.unifiedpush.android.distributor.ipc.InternalOpcode
|
||||||
|
|
||||||
class ThemeViewModel(val messenger: InternalMessenger?, val application: Application?) : ViewModel() {
|
class ThemeViewModel(val messenger: InternalMessenger?, val application: Application?) : ViewModel() {
|
||||||
var dynamicColors by mutableStateOf(application?.let { AppStore(it).dynamicColors } ?: false)
|
var dynamicColors by mutableStateOf(application?.let { PrismPreferences(it).dynamicColors } ?: false)
|
||||||
|
|
||||||
fun toggleDynamicColors() {
|
fun toggleDynamicColors() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
dynamicColors = !dynamicColors
|
dynamicColors = !dynamicColors
|
||||||
application?.let { AppStore(it).dynamicColors = dynamicColors }
|
application?.let { PrismPreferences(it).dynamicColors = dynamicColors }
|
||||||
messenger?.sendIMessage(InternalOpcode.THEME_DYN_SET, if (dynamicColors) 1 else 0)
|
messenger?.sendIMessage(InternalOpcode.THEME_DYN_SET, if (dynamicColors) 1 else 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.R
|
import app.lonecloud.prism.R
|
||||||
import app.lonecloud.prism.activities.MainViewModel
|
import app.lonecloud.prism.activities.MainViewModel
|
||||||
import app.lonecloud.prism.activities.PreviewFactory
|
import app.lonecloud.prism.activities.PreviewFactory
|
||||||
|
|
@ -89,7 +89,7 @@ fun MainScreen(
|
||||||
"RefreshRegistrations" -> viewModel.refreshRegistrations()
|
"RefreshRegistrations" -> viewModel.refreshRegistrations()
|
||||||
"UpdatePrismServerConfigured" -> {
|
"UpdatePrismServerConfigured" -> {
|
||||||
viewModel.application?.let { app ->
|
viewModel.application?.let { app ->
|
||||||
val store = AppStore(app)
|
val store = PrismPreferences(app)
|
||||||
viewModel.updatePrismServerConfigured(
|
viewModel.updatePrismServerConfigured(
|
||||||
!store.prismServerUrl.isNullOrBlank() &&
|
!store.prismServerUrl.isNullOrBlank() &&
|
||||||
!store.prismApiKey.isNullOrBlank()
|
!store.prismApiKey.isNullOrBlank()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.text.ClickableText
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
|
@ -24,7 +25,12 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.lonecloud.prism.R
|
import app.lonecloud.prism.R
|
||||||
|
|
||||||
|
|
@ -52,7 +58,7 @@ fun PrismServerConfigButton(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = if (currentUrl.isNotBlank()) {
|
text = if (currentUrl.isNotBlank()) {
|
||||||
stringResource(R.string.prism_server_configured, currentUrl)
|
currentUrl
|
||||||
} else {
|
} else {
|
||||||
stringResource(R.string.prism_server_not_configured)
|
stringResource(R.string.prism_server_not_configured)
|
||||||
},
|
},
|
||||||
|
|
@ -87,7 +93,9 @@ fun PrismServerConfigDialog(
|
||||||
var isTesting by remember { mutableStateOf(false) }
|
var isTesting by remember { mutableStateOf(false) }
|
||||||
var testResult by remember { mutableStateOf<String?>(null) }
|
var testResult by remember { mutableStateOf<String?>(null) }
|
||||||
var showServerChangeWarning by remember { mutableStateOf(false) }
|
var showServerChangeWarning by remember { mutableStateOf(false) }
|
||||||
|
var showClearConfirmation by remember { mutableStateOf(false) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
|
|
@ -97,6 +105,46 @@ fun PrismServerConfigDialog(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
val description = stringResource(R.string.prism_server_description)
|
||||||
|
val repoUrl = stringResource(R.string.prism_server_repo_link)
|
||||||
|
val fullText = "$description\n\n$repoUrl"
|
||||||
|
val annotatedString = buildAnnotatedString {
|
||||||
|
append(description)
|
||||||
|
append("\n\n")
|
||||||
|
|
||||||
|
val linkStart = length
|
||||||
|
withStyle(
|
||||||
|
style = SpanStyle(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
textDecoration = TextDecoration.Underline
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
append(repoUrl)
|
||||||
|
}
|
||||||
|
addStringAnnotation(
|
||||||
|
tag = "URL",
|
||||||
|
annotation = repoUrl,
|
||||||
|
start = linkStart,
|
||||||
|
end = length
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickableText(
|
||||||
|
text = annotatedString,
|
||||||
|
style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
),
|
||||||
|
onClick = { offset ->
|
||||||
|
annotatedString.getStringAnnotations(
|
||||||
|
tag = "URL",
|
||||||
|
start = offset,
|
||||||
|
end = offset
|
||||||
|
).firstOrNull()?.let { annotation ->
|
||||||
|
uriHandler.openUri(annotation.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = url,
|
value = url,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
|
|
@ -163,7 +211,7 @@ fun PrismServerConfigDialog(
|
||||||
.count { it.description?.startsWith("target:") == true }
|
.count { it.description?.startsWith("target:") == true }
|
||||||
if (manualAppsCount > 0) {
|
if (manualAppsCount > 0) {
|
||||||
val oldUrl = initialUrl
|
val oldUrl = initialUrl
|
||||||
val oldKey = app.lonecloud.prism.AppStore(context).prismApiKey
|
val oldKey = app.lonecloud.prism.PrismPreferences(context).prismApiKey
|
||||||
if (!oldUrl.isNullOrBlank() && !oldKey.isNullOrBlank()) {
|
if (!oldUrl.isNullOrBlank() && !oldKey.isNullOrBlank()) {
|
||||||
app.lonecloud.prism.PrismServerClient.deleteAllApps(
|
app.lonecloud.prism.PrismServerClient.deleteAllApps(
|
||||||
context,
|
context,
|
||||||
|
|
@ -197,12 +245,76 @@ fun PrismServerConfigDialog(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
if (initialUrl.isNotBlank()) {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showClearConfirmation = true },
|
||||||
|
enabled = !isTesting
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.clear_server_button),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
TextButton(onClick = onDismiss, enabled = !isTesting) {
|
TextButton(onClick = onDismiss, enabled = !isTesting) {
|
||||||
Text(stringResource(R.string.cancel_button))
|
Text(stringResource(R.string.cancel_button))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (showClearConfirmation) {
|
||||||
|
val db = app.lonecloud.prism.DatabaseFactory.getDb(context)
|
||||||
|
val manualAppsCount = db.listApps()
|
||||||
|
.count { it.description?.startsWith("target:") == true }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showClearConfirmation = false },
|
||||||
|
title = { Text(stringResource(R.string.clear_server_confirm_title)) },
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
if (manualAppsCount > 0) {
|
||||||
|
stringResource(R.string.clear_server_confirm_message_with_apps, manualAppsCount)
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.clear_server_confirm_message_no_apps)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (initialUrl.isNotBlank()) {
|
||||||
|
val oldKey = app.lonecloud.prism.PrismPreferences(context).prismApiKey
|
||||||
|
if (!oldKey.isNullOrBlank() && manualAppsCount > 0) {
|
||||||
|
app.lonecloud.prism.PrismServerClient.deleteAllApps(
|
||||||
|
context,
|
||||||
|
serverUrl = initialUrl,
|
||||||
|
apiKey = oldKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSave("", "")
|
||||||
|
showClearConfirmation = false
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.clear_server_button))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showClearConfirmation = false }) {
|
||||||
|
Text(stringResource(R.string.cancel_button))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (showServerChangeWarning) {
|
if (showServerChangeWarning) {
|
||||||
val db = app.lonecloud.prism.DatabaseFactory.getDb(context)
|
val db = app.lonecloud.prism.DatabaseFactory.getDb(context)
|
||||||
val manualAppsCount = db.listApps()
|
val manualAppsCount = db.listApps()
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ fun RestartServicesPreference(onClick: () -> Unit) {
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.restart_service_button),
|
text = stringResource(R.string.reset_connection_button),
|
||||||
style = MaterialTheme.typography.bodyLarge
|
style = MaterialTheme.typography.bodyLarge
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package app.lonecloud.prism.activities.ui
|
package app.lonecloud.prism.activities.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
|
|
||||||
data class SettingsState(
|
data class SettingsState(
|
||||||
val showToasts: Boolean,
|
val showToasts: Boolean,
|
||||||
|
|
@ -10,7 +10,7 @@ data class SettingsState(
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun from(context: Context): SettingsState {
|
fun from(context: Context): SettingsState {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
return SettingsState(
|
return SettingsState(
|
||||||
showToasts = store.showToasts,
|
showToasts = store.showToasts,
|
||||||
prismServerUrl = store.prismServerUrl ?: "",
|
prismServerUrl = store.prismServerUrl ?: "",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import android.os.Looper
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.DatabaseFactory
|
import app.lonecloud.prism.DatabaseFactory
|
||||||
import app.lonecloud.prism.Distributor
|
import app.lonecloud.prism.Distributor
|
||||||
import app.lonecloud.prism.Distributor.sendMessage
|
import app.lonecloud.prism.Distributor.sendMessage
|
||||||
|
|
@ -32,7 +32,7 @@ import org.unifiedpush.android.distributor.ChannelCreationStatus
|
||||||
|
|
||||||
class ServerConnection(private val context: Context, private val releaseLock: () -> Unit) : WebSocketListener() {
|
class ServerConnection(private val context: Context, private val releaseLock: () -> Unit) : WebSocketListener() {
|
||||||
|
|
||||||
private val store = AppStore(context)
|
private val store = PrismPreferences(context)
|
||||||
|
|
||||||
fun start(): WebSocket {
|
fun start(): WebSocket {
|
||||||
val client = OkHttpClient.Builder()
|
val client = OkHttpClient.Builder()
|
||||||
|
|
@ -107,8 +107,6 @@ class ServerConnection(private val context: Context, private val releaseLock: ()
|
||||||
}
|
}
|
||||||
db.deleteDisabledApps()
|
db.deleteDisabledApps()
|
||||||
} else {
|
} else {
|
||||||
// We remove pending unregistrations
|
|
||||||
// and register pending registrations
|
|
||||||
db.listDisabledChannelIds().forEach {
|
db.listDisabledChannelIds().forEach {
|
||||||
Log.d(TAG, "Hello, unregistering $it")
|
Log.d(TAG, "Hello, unregistering $it")
|
||||||
ClientMessage.Unregister(channelID = it).send(webSocket)
|
ClientMessage.Unregister(channelID = it).send(webSocket)
|
||||||
|
|
@ -226,7 +224,6 @@ class ServerConnection(private val context: Context, private val releaseLock: ()
|
||||||
if (failToUseUrlCandidate(context)) return
|
if (failToUseUrlCandidate(context)) return
|
||||||
if (!shouldRestart()) return
|
if (!shouldRestart()) return
|
||||||
if (SourceManager.addFail(context, webSocket)) {
|
if (SourceManager.addFail(context, webSocket)) {
|
||||||
// If null, we keep the worker with its 16min
|
|
||||||
val delay = SourceManager.getTimeout() ?: return
|
val delay = SourceManager.getTimeout() ?: return
|
||||||
Log.d(TAG, "Retrying in $delay ms")
|
Log.d(TAG, "Retrying in $delay ms")
|
||||||
RestartWorker.run(context, delay = delay)
|
RestartWorker.run(context, delay = delay)
|
||||||
|
|
@ -262,7 +259,6 @@ class ServerConnection(private val context: Context, private val releaseLock: ()
|
||||||
}
|
}
|
||||||
if (!NetworkCallbackFactory.hasInternet()) {
|
if (!NetworkCallbackFactory.hasInternet()) {
|
||||||
Log.d(TAG, "No Internet: do not restart")
|
Log.d(TAG, "No Internet: do not restart")
|
||||||
// It will be restarted when Internet is back
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ package app.lonecloud.prism.receivers
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
|
|
||||||
class PrismConfigReceiver : BroadcastReceiver() {
|
class PrismConfigReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val store = AppStore(context)
|
val store = PrismPreferences(context)
|
||||||
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_SET_PRISM_SERVER_URL -> {
|
ACTION_SET_PRISM_SERVER_URL -> {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
package app.lonecloud.prism.receivers
|
package app.lonecloud.prism.receivers
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.Distributor
|
import app.lonecloud.prism.Distributor
|
||||||
import app.lonecloud.prism.callback.NetworkCallbackFactory
|
import app.lonecloud.prism.callback.NetworkCallbackFactory
|
||||||
import org.unifiedpush.android.distributor.receiver.DistributorReceiver
|
import org.unifiedpush.android.distributor.receiver.DistributorReceiver
|
||||||
|
|
@ -16,5 +16,5 @@ class RegisterBroadcastReceiver : DistributorReceiver() {
|
||||||
|
|
||||||
override fun hasInternet(context: Context): Boolean = NetworkCallbackFactory.hasInternet()
|
override fun hasInternet(context: Context): Boolean = NetworkCallbackFactory.hasInternet()
|
||||||
|
|
||||||
override fun showToasts(context: Context): Boolean = AppStore(context).showToasts
|
override fun showToasts(context: Context): Boolean = PrismPreferences(context).showToasts
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package app.lonecloud.prism.services
|
package app.lonecloud.prism.services
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.Distributor
|
import app.lonecloud.prism.Distributor
|
||||||
import org.unifiedpush.android.distributor.MigrationManager as MManager
|
import org.unifiedpush.android.distributor.MigrationManager as MManager
|
||||||
|
|
||||||
class MigrationManager : MManager() {
|
class MigrationManager : MManager() {
|
||||||
override val distrib = Distributor
|
override val distrib = Distributor
|
||||||
override fun getStore(context: Context): MigrationStore = AppStore(context)
|
override fun getStore(context: Context): MigrationStore = PrismPreferences(context)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package app.lonecloud.prism.services
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.DatabaseFactory
|
import app.lonecloud.prism.DatabaseFactory
|
||||||
import app.lonecloud.prism.Distributor
|
import app.lonecloud.prism.Distributor
|
||||||
import org.unifiedpush.android.distributor.Database
|
import org.unifiedpush.android.distributor.Database
|
||||||
|
|
@ -28,7 +28,7 @@ class PrismInternalService : InternalService() {
|
||||||
override val distributor: UnifiedPushDistributor = Distributor
|
override val distributor: UnifiedPushDistributor = Distributor
|
||||||
override val db: Database by lazy { DatabaseFactory.getDb(this) }
|
override val db: Database by lazy { DatabaseFactory.getDb(this) }
|
||||||
|
|
||||||
private val appStore by lazy { AppStore(this) }
|
private val appStore by lazy { PrismPreferences(this) }
|
||||||
|
|
||||||
override var themeDynamicColors: Boolean
|
override var themeDynamicColors: Boolean
|
||||||
get() = appStore.dynamicColors
|
get() = appStore.dynamicColors
|
||||||
|
|
@ -44,9 +44,7 @@ class PrismInternalService : InternalService() {
|
||||||
|
|
||||||
override fun getDebugInfo(): String = "Prism Distributor"
|
override fun getDebugInfo(): String = "Prism Distributor"
|
||||||
|
|
||||||
override fun runAppMigration() {
|
override fun runAppMigration() {}
|
||||||
// No app migration needed for Prism currently
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun account(): IAccount = object : IAccount {
|
override fun account(): IAccount = object : IAccount {
|
||||||
override fun get(): String? = null
|
override fun get(): String? = null
|
||||||
|
|
@ -55,22 +53,20 @@ class PrismInternalService : InternalService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun api(): IApi = object : IApi {
|
override fun api(): IApi = object : IApi {
|
||||||
override fun newPushServer(url: String?) {
|
override fun newPushServer(url: String?) {}
|
||||||
// Prism uses fixed Mozilla server, but can be customized
|
|
||||||
}
|
|
||||||
override fun getUrl(): String = appStore.apiUrl
|
override fun getUrl(): String = appStore.apiUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registrations() = object : IRegistrations {
|
override fun registrations() = object : IRegistrations {
|
||||||
override fun delete(registrations: List<String>) {
|
override fun delete(registrations: List<String>) {
|
||||||
registrations.forEach { token ->
|
registrations.forEach { token ->
|
||||||
distributor.deleteApp(context, token)
|
distributor.deleteApp(this@PrismInternalService, token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun list(): List<App> = db
|
override fun list(): List<App> = db
|
||||||
.listApps().map {
|
.listApps().map {
|
||||||
val pm = context.packageManager
|
val pm = this@PrismInternalService.packageManager
|
||||||
|
|
||||||
val isManualApp = it.description?.startsWith("target:") == true
|
val isManualApp = it.description?.startsWith("target:") == true
|
||||||
val targetPackage = if (isManualApp) {
|
val targetPackage = if (isManualApp) {
|
||||||
|
|
@ -96,22 +92,20 @@ class PrismInternalService : InternalService() {
|
||||||
vapidKey = it.vapidKey,
|
vapidKey = it.vapidKey,
|
||||||
title = displayTitle,
|
title = displayTitle,
|
||||||
msgCount = it.msgCount,
|
msgCount = it.msgCount,
|
||||||
description = if (it.packageName == context.packageName) {
|
description = if (it.packageName == this@PrismInternalService.packageName) {
|
||||||
Description.LocalChannel
|
Description.LocalChannel
|
||||||
} else {
|
} else {
|
||||||
Description.StringDescription(packageToResolve)
|
Description.StringDescription(packageToResolve)
|
||||||
},
|
},
|
||||||
icon = getApplicationIcon(packageToResolve)?.toBitmap(),
|
icon = getApplicationIcon(packageToResolve)?.toBitmap(),
|
||||||
isLocal = it.packageName == context.packageName
|
isLocal = it.packageName == this@PrismInternalService.packageName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copyEndpoint(token: String?) {
|
override fun copyEndpoint(token: String?) {
|
||||||
super@PrismInternalService.registrations().copyEndpoint(token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addLocal(title: String) {
|
override fun addLocal(title: String) {
|
||||||
super@PrismInternalService.registrations().addLocal(title)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ package app.lonecloud.prism.services
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import app.lonecloud.prism.AppStore
|
import app.lonecloud.prism.PrismPreferences
|
||||||
import app.lonecloud.prism.Distributor
|
import app.lonecloud.prism.Distributor
|
||||||
import app.lonecloud.prism.api.MessageSender
|
import app.lonecloud.prism.api.MessageSender
|
||||||
import app.lonecloud.prism.callback.NetworkCallbackFactory
|
import app.lonecloud.prism.callback.NetworkCallbackFactory
|
||||||
|
|
@ -20,7 +20,6 @@ class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params
|
||||||
*/
|
*/
|
||||||
@Suppress("ReturnCount")
|
@Suppress("ReturnCount")
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
// We avoid running twice at the same time
|
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
Log.d(TAG, "Working [$id]")
|
Log.d(TAG, "Working [$id]")
|
||||||
if (!NetworkCallbackFactory.hasInternet()) {
|
if (!NetworkCallbackFactory.hasInternet()) {
|
||||||
|
|
@ -29,8 +28,6 @@ class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params
|
||||||
}
|
}
|
||||||
if (SourceManager.isRunningWithoutFailure) {
|
if (SourceManager.isRunningWithoutFailure) {
|
||||||
Log.d(TAG, "Running without failure")
|
Log.d(TAG, "Running without failure")
|
||||||
// We send a ping, if it fails it will restart this worker, and wont
|
|
||||||
// pass this check
|
|
||||||
MessageSender.ping(applicationContext)
|
MessageSender.ping(applicationContext)
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
@ -44,10 +41,7 @@ class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params
|
||||||
private val lock = Object()
|
private val lock = Object()
|
||||||
|
|
||||||
override fun canRun(context: Context): Boolean {
|
override fun canRun(context: Context): Boolean {
|
||||||
// We don't have any credential requirement, if we don't have
|
return !PrismPreferences(context).migrated
|
||||||
// a uaid yet, it will be created during the initial sync
|
|
||||||
// So, as soon as the user hasn't migrated, we can run
|
|
||||||
return !AppStore(context).migrated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isServiceStarted(context: Context): Boolean = FgService.isServiceStarted()
|
override fun isServiceStarted(context: Context): Boolean = FgService.isServiceStarted()
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,10 @@
|
||||||
<string name="add_manual_app_content_description">Add manual app</string>
|
<string name="add_manual_app_content_description">Add manual app</string>
|
||||||
<string name="debug_title">Debug Information</string>
|
<string name="debug_title">Debug Information</string>
|
||||||
<string name="configure_server">Configure Prism Server</string>
|
<string name="configure_server">Configure Prism Server</string>
|
||||||
|
<string name="prism_server_description">Self-host a Prism server to unlock manual app registrations. Otherwise, this distributor works normally with UnifiedPush apps.</string>
|
||||||
|
<string name="prism_server_repo_link">https://github.com/lone-cloud/prism</string>
|
||||||
<string name="prism_server_configured">Prism server configured</string>
|
<string name="prism_server_configured">Prism server configured</string>
|
||||||
|
<string name="prism_server_configured_with_version">%1$s (v%2$s)</string>
|
||||||
<string name="prism_server_not_configured">Prism server not configured</string>
|
<string name="prism_server_not_configured">Prism server not configured</string>
|
||||||
<string name="prism_server_url_label">Server URL</string>
|
<string name="prism_server_url_label">Server URL</string>
|
||||||
<string name="prism_server_url_placeholder">https://prism.example.com</string>
|
<string name="prism_server_url_placeholder">https://prism.example.com</string>
|
||||||
|
|
@ -57,7 +60,11 @@
|
||||||
<string name="connection_successful">Connection successful</string>
|
<string name="connection_successful">Connection successful</string>
|
||||||
<string name="connection_failed">Connection failed</string>
|
<string name="connection_failed">Connection failed</string>
|
||||||
<string name="test_and_save_button">Test and Save</string>
|
<string name="test_and_save_button">Test and Save</string>
|
||||||
<string name="restart_service_button">Restart Service</string>
|
<string name="clear_server_button">Clear</string>
|
||||||
|
<string name="clear_server_confirm_title">Remove Prism Server?</string>
|
||||||
|
<string name="clear_server_confirm_message_no_apps">This will remove your Prism server configuration.</string>
|
||||||
|
<string name="clear_server_confirm_message_with_apps">You have %d manual app registration(s). Clearing the server will delete them from the server and remove the configuration.</string>
|
||||||
|
<string name="reset_connection_button">Reset Connection</string>
|
||||||
<string name="app_dropdown_show_toasts">Notify when apps register</string>
|
<string name="app_dropdown_show_toasts">Notify when apps register</string>
|
||||||
<string name="dynamic_colors_title">Dynamic Colors</string>
|
<string name="dynamic_colors_title">Dynamic Colors</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue