Use new UI from library

This commit is contained in:
sim 2026-02-10 14:24:02 +01:00
parent 9c39c0ad6e
commit 1ae48fdfc1
27 changed files with 154 additions and 1193 deletions

View file

@ -21,6 +21,7 @@
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:process=":ui"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -34,6 +35,12 @@
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="This service needs to be constantly connected to the server, to get Server-Sent Events messages from it."/> android:value="This service needs to be constantly connected to the server, to get Server-Sent Events messages from it."/>
</service> </service>
<service android:name=".services.InternalServiceImpl"
android:exported="false">
<intent-filter>
<action android:name="org.unifiedpush.distributor.internal.service"/>
</intent-filter>
</service>
<receiver <receiver
android:name=".receivers.StartReceiver" android:name=".receivers.StartReceiver"

View file

@ -5,12 +5,13 @@ import org.unifiedpush.distributor.Database
import org.unifiedpush.distributor.UnifiedPushDistributor import org.unifiedpush.distributor.UnifiedPushDistributor
import org.unifiedpush.distributor.sunup.api.MessageSender import org.unifiedpush.distributor.sunup.api.MessageSender
import org.unifiedpush.distributor.sunup.api.data.ClientMessage import org.unifiedpush.distributor.sunup.api.data.ClientMessage
import org.unifiedpush.distributor.sunup.receivers.RegisterBroadcastReceiver
/** /**
* These functions are used to send messages to other apps * These functions are used to send messages to other apps
*/ */
object Distributor : UnifiedPushDistributor() { object Distributor : UnifiedPushDistributor() {
override val receiverComponentName = "org.unifiedpush.distributor.sunup.receivers.RegisterBroadcastReceiver" override val receiverComponent = RegisterBroadcastReceiver::class.java
override fun getDb(context: Context): Database = DatabaseFactory.getDb(context) override fun getDb(context: Context): Database = DatabaseFactory.getDb(context)

View file

@ -0,0 +1,34 @@
package org.unifiedpush.distributor.sunup
import org.unifiedpush.android.distributor.ui.AppConfig
import org.unifiedpush.android.distributor.ui.InAppNotifsConfig
import org.unifiedpush.android.distributor.ui.LoginConfig
import org.unifiedpush.android.distributor.ui.MigrationConfig
import org.unifiedpush.android.distributor.ui.NoLoginConfig
import org.unifiedpush.android.distributor.ui.PrivacyPolicy
class SunupAppConfig : AppConfig {
override val appName: Int
get() = R.string.app_name
override val restartableService = true
override val privacyPolicy: PrivacyPolicy
get() = object : PrivacyPolicy {
override val description: Int
get() = R.string.sunup_privacy_policy
override val linkText: String
get() = "https://www.mozilla.org/en-US/privacy/firefox/"
override val linkTarget: String
get() = "https://www.mozilla.org/en-US/privacy/firefox/#types-of-data-defined"
}
override val loginConfig: LoginConfig
get() = NoLoginConfig(canChangeUrl = true)
override val migrationConfig: MigrationConfig?
get() = if (BuildConfig.SUPPORT_MIGRATIONS) {
object : MigrationConfig {
override val supportTempFallback = true
}
} else {
null
}
override val inAppNotifsConfig: InAppNotifsConfig? = null
}

View file

@ -1,97 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.content.Context
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.Distributor
import org.unifiedpush.distributor.sunup.EventBus
import org.unifiedpush.distributor.sunup.api.ApiUrlCandidate
import org.unifiedpush.distributor.sunup.services.FgService
import org.unifiedpush.distributor.sunup.services.MigrationManager
import org.unifiedpush.distributor.sunup.services.RestartWorker
import org.unifiedpush.distributor.sunup.services.SourceManager
import org.unifiedpush.distributor.sunup.utils.TAG
class AppAction(private val action: Action) {
sealed class Action {
data object RestartService : Action()
class NewPushServer(val url: String) : Action()
class ShowToasts(val enable: Boolean) : Action()
class DeleteRegistration(val registrations: List<String>) : Action()
data object FallbackIntroShown : Action()
class FallbackDistribSelected(val distributor: String?) : Action()
class MigrateToDistrib(val distributor: String) : Action()
data object ReactivateUnifiedPush : Action()
}
fun handle(context: Context) {
when (action) {
is Action.RestartService -> restartService(context)
is Action.NewPushServer -> newPushServer(context, action)
is Action.ShowToasts -> showToasts(context, action)
is Action.DeleteRegistration -> deleteRegistration(context, action)
is Action.FallbackIntroShown -> fallbackIntroShown(context)
is Action.FallbackDistribSelected -> fallbackDistribSelected(context, action)
is Action.MigrateToDistrib -> migrateToDistrib(context, action)
is Action.ReactivateUnifiedPush -> reactivateUnifiedPush(context)
}
}
private fun restartService(context: Context) {
Log.d(TAG, "Restarting the Listener")
SourceManager.clearFails()
FgService.stopService {
RestartWorker.run(context, delay = 0)
}
}
private fun newPushServer(context: Context, action: Action.NewPushServer) {
ApiUrlCandidate.test(context, action.url)
}
private fun showToasts(context: Context, action: Action.ShowToasts) {
AppStore(context).showToasts = action.enable
}
private fun deleteRegistration(context: Context, action: Action.DeleteRegistration) {
action.registrations.forEach {
Distributor.deleteApp(context, it)
}
}
private fun fallbackIntroShown(context: Context) {
MigrationManager().setFallbackIntroShown(context)
}
/**
* Save fallback service
*
* If fallback is disabled and we have already send TEMP_UNAVAILABLE:
* we send the endpoint again
*/
private fun fallbackDistribSelected(context: Context, action: Action.FallbackDistribSelected) {
MigrationManager()
.selectFallbackService(
context,
action.distributor,
SourceManager.shouldSendFallback
)
}
private fun migrateToDistrib(context: Context, action: Action.MigrateToDistrib) {
MigrationManager().migrate(context, action.distributor)
}
private fun reactivateUnifiedPush(context: Context) {
MigrationManager().reactivate(context)
}
}
fun ViewModel.publishAction(action: AppAction) {
viewModelScope.launch {
EventBus.publish(action)
}
}

View file

@ -1,25 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.content.Context
import org.unifiedpush.android.distributor.ui.compose.state.ApplicationRowState
import org.unifiedpush.distributor.utils.appInfoForMetadata
import org.unifiedpush.distributor.utils.getApplicationIcon
import org.unifiedpush.distributor.utils.getApplicationName
fun Context.applicationRowState(packageName: String, description: String? = null): ApplicationRowState {
val ai = appInfoForMetadata(packageName)
val title = ai?.let { getApplicationName(it) } ?: packageName
val icon = getApplicationIcon(packageName)
val description = if (title == packageName) {
description ?: ""
} else {
description?.let { "$it$packageName" }
?: packageName
}
return ApplicationRowState(
icon = icon,
title = title,
packageName = packageName,
description = description
)
}

View file

@ -1,73 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.app.Application
import android.content.Context
import org.unifiedpush.android.distributor.ui.compose.DistribMigrationViewModel as UPDistribMigrationViewModel
import org.unifiedpush.android.distributor.ui.compose.state.DistribMigrationState
import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.BuildConfig
import org.unifiedpush.distributor.utils.listOtherDistributors
class DistribMigrationViewModel(state: DistribMigrationState, val application: Application? = null) : UPDistribMigrationViewModel(state) {
constructor(application: Application) : this(
stateFrom(application),
application
)
override fun onFallbackDistribSelected(distributor: String?) {
publishAction(
AppAction(AppAction.Action.FallbackDistribSelected(distributor))
)
}
override fun onMigrationDistributorSelected(distributor: String) {
publishAction(
AppAction(AppAction.Action.MigrateToDistrib(distributor))
)
}
override fun onFallbackIntroShown() {
publishAction(
AppAction(AppAction.Action.FallbackIntroShown)
)
}
override fun onServiceReactivated() {
publishAction(
AppAction(AppAction.Action.ReactivateUnifiedPush)
)
}
override fun refreshDistributors() {
application?.let { context ->
refreshDistributors {
val store = AppStore(context)
val fallbackDistrib = store.fallbackService
return@refreshDistributors context.listOtherDistributors()
.map { packageName ->
context.applicationRowState(packageName).copy(
selected = fallbackDistrib == packageName
)
}.toSet()
}
}
}
companion object {
fun stateFrom(context: Context): DistribMigrationState {
val store = AppStore(context)
val fallbackDistrib = store.fallbackService
val distributors = context.listOtherDistributors().map { packageName ->
context.applicationRowState(packageName).copy(
selected = fallbackDistrib == packageName
)
}.toSet()
return DistribMigrationState(
distributors,
store.fallbackIntroShown,
migrated = store.migrated,
featureEnabled = BuildConfig.SUPPORT_MIGRATIONS
)
}
}
}

View file

@ -6,53 +6,51 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.CoroutineScope import kotlin.system.exitProcess
import kotlinx.coroutines.Dispatchers import org.unifiedpush.android.distributor.ui.screen.App
import kotlinx.coroutines.Job import org.unifiedpush.android.distributor.ui.vm.ThemeViewModel
import kotlinx.coroutines.launch import org.unifiedpush.android.distributor.ui.vm.ViewModelFactory
import org.unifiedpush.distributor.sunup.EventBus import org.unifiedpush.distributor.ipc.InternalMessenger
import org.unifiedpush.distributor.ipc.subscribeUiActions
import org.unifiedpush.distributor.sunup.Migrations import org.unifiedpush.distributor.sunup.Migrations
import org.unifiedpush.distributor.sunup.activities.ThemeViewModel import org.unifiedpush.distributor.sunup.SunupAppConfig
import org.unifiedpush.distributor.sunup.activities.ui.App
import org.unifiedpush.distributor.sunup.activities.ui.theme.AppTheme import org.unifiedpush.distributor.sunup.activities.ui.theme.AppTheme
import org.unifiedpush.distributor.sunup.services.RestartWorker
import org.unifiedpush.distributor.sunup.utils.TAG import org.unifiedpush.distributor.sunup.utils.TAG
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private var jobs: MutableList<Job> = emptyList<Job>().toMutableList()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
RestartWorker.startPeriodic(this)
Migrations(this).run() Migrations(this).run()
val messenger = InternalMessenger(this)
val uiFlow = subscribeUiActions(this)
val appConfig = SunupAppConfig()
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
val factory = ViewModelFactory(this.application) val factory = ViewModelFactory(
context = this,
appConfig = appConfig,
messenger = messenger
)
val themeViewModel = viewModel<ThemeViewModel>(factory = factory) val themeViewModel = viewModel<ThemeViewModel>(factory = factory)
AppTheme( AppTheme(
dynamicColor = themeViewModel.dynamicColors dynamicColor = themeViewModel.dynamicColors
) { ) {
App(factory, themeViewModel) App(
appConfig = appConfig,
factory = factory,
themeViewModel = themeViewModel,
uiFlow = uiFlow
)
} }
subscribeActions()
}
}
private fun subscribeActions() {
Log.d(TAG, "Subscribing to actions")
jobs += CoroutineScope(Dispatchers.IO).launch {
EventBus.subscribe<AppAction> { it.handle(this@MainActivity) }
} }
} }
override fun onDestroy() { override fun onDestroy() {
Log.d(TAG, "Destroy") Log.d(TAG, "Destroy")
jobs.removeAll {
it.cancel()
true
}
super.onDestroy() super.onDestroy()
exitProcess(0)
} }
} }

View file

@ -1,89 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.unifiedpush.android.distributor.ui.compose.BatteryOptimisationViewModel
import org.unifiedpush.android.distributor.ui.compose.RegistrationsViewModel
import org.unifiedpush.android.distributor.ui.compose.state.RegistrationListState
import org.unifiedpush.distributor.sunup.activities.ui.MainUiState
class MainViewModel(
mainUiState: MainUiState,
val batteryOptimisationViewModel: BatteryOptimisationViewModel,
val registrationsViewModel: RegistrationsViewModel,
val application: Application? = null
) : ViewModel() {
constructor(application: Application) : this(
mainUiState = MainUiState(),
batteryOptimisationViewModel = BatteryOptimisationViewModel(application),
registrationsViewModel = RegistrationsViewModel(
getRegistrationListState(application)
),
application
)
var mainUiState by mutableStateOf(mainUiState)
private set
private var lastDebugClickTime by mutableLongStateOf(0L)
private var debugClickCount by mutableIntStateOf(0)
fun closePermissionDialog() {
viewModelScope.launch {
mainUiState = mainUiState.copy(showPermissionDialog = false)
}
}
fun refreshRegistrations() {
viewModelScope.launch {
application?.let {
registrationsViewModel.state = getRegistrationListState(it)
}
}
}
fun deleteSelection() {
viewModelScope.launch {
val state = registrationsViewModel.state
val tokenList = state.list.filter { it.selected }.map { it.token }
publishAction(
AppAction(AppAction.Action.DeleteRegistration(tokenList))
)
registrationsViewModel.state = RegistrationListState(
list = state.list.filter {
!it.selected
},
selectionCount = 0
)
}
}
fun addDebugClick() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastDebugClickTime < 500) {
debugClickCount++
if (debugClickCount == 5) {
mainUiState = mainUiState.copy(showDebugInfo = true)
}
} else {
debugClickCount = 1
}
lastDebugClickTime = currentTime
}
fun dismissDebugInfo() {
mainUiState = mainUiState.copy(showDebugInfo = false)
}
fun restartService() {
publishAction(AppAction(AppAction.Action.RestartService))
}
}

View file

@ -1,20 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.content.Context
import org.unifiedpush.android.distributor.ui.compose.state.RegistrationListState
import org.unifiedpush.android.distributor.ui.compose.state.RegistrationState
import org.unifiedpush.distributor.Database
import org.unifiedpush.distributor.sunup.DatabaseFactory
fun getRegistrationListState(context: Context): RegistrationListState = RegistrationListState(
list = DatabaseFactory.getDb(context).listApps().map { app ->
getRegistrationState(context, app)
}
)
fun getRegistrationState(context: Context, app: Database.App): RegistrationState = RegistrationState(
app = context.applicationRowState(app.packageName, app.description),
msgCount = app.msgCount,
token = app.connectorToken,
copyable = false
)

View file

@ -1,74 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.app.Application
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import java.net.URL
import kotlinx.coroutines.launch
import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.BuildConfig
import org.unifiedpush.distributor.sunup.activities.ui.SettingsState
import org.unifiedpush.distributor.sunup.utils.TAG
class SettingsViewModel(state: SettingsState, val application: Application? = null) : ViewModel() {
constructor(application: Application) : this(
SettingsState.from(application),
application
)
var state by mutableStateOf(state)
private set
fun toggleChangeServer() {
viewModelScope.launch {
state = state.copy(showChangeServerDialog = !state.showChangeServerDialog)
}
}
fun togglePrivacyPolicy() {
viewModelScope.launch {
state = state.copy(showPrivacyPolicy = !state.showPrivacyPolicy)
}
}
fun newPushServer(url: String) {
var url = url
viewModelScope.launch {
try {
if (url.isBlank()) {
url = BuildConfig.DEFAULT_API_URL
}
if (url.slice(0..4) !in arrayOf("http:", "https", "ws://", "wss:/")) {
url = "https://$url"
}
URL(url)
state = state.copy(
currentApiUrl = url,
showChangeServerDialog = false
)
publishAction(AppAction(AppAction.Action.NewPushServer(url)))
} catch (e: Exception) {
Log.d(TAG, "Ignoring url: $url : $e:w")
}
}
}
fun refreshApiUrl() {
viewModelScope.launch {
application?.let {
state = state.copy(currentApiUrl = AppStore(it).apiUrl)
}
}
}
fun toggleShowToasts() {
viewModelScope.launch {
state = state.copy(showToasts = !state.showToasts)
publishAction(AppAction(AppAction.Action.ShowToasts(state.showToasts)))
}
}
}

View file

@ -1,25 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.unifiedpush.distributor.sunup.AppStore
class ThemeViewModel(val application: Application? = null) : ViewModel() {
var dynamicColors by mutableStateOf(
application?.let { AppStore(it).dynamicColors } ?: false
)
fun toggleDynamicColors() {
viewModelScope.launch {
dynamicColors = !dynamicColors
application?.run {
AppStore(this).dynamicColors = dynamicColors
}
}
}
}

View file

@ -1,25 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.unifiedpush.distributor.sunup.EventBus
class UiAction(val action: Action) {
enum class Action {
RefreshRegistrations,
RefreshApiUrl
}
fun handle(action: (Action) -> Unit) {
action(this.action)
}
companion object {
fun publish(type: Action) {
CoroutineScope(Dispatchers.IO).launch {
EventBus.publish(UiAction(type))
}
}
}
}

View file

@ -1,50 +0,0 @@
package org.unifiedpush.distributor.sunup.activities
import android.app.Application
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.unifiedpush.android.distributor.ui.compose.BatteryOptimisationViewModel
import org.unifiedpush.android.distributor.ui.compose.previewRegistrationsViewModel
import org.unifiedpush.android.distributor.ui.compose.state.DistribMigrationState
import org.unifiedpush.distributor.sunup.BuildConfig
import org.unifiedpush.distributor.sunup.activities.ThemeViewModel
import org.unifiedpush.distributor.sunup.activities.ui.MainUiState
import org.unifiedpush.distributor.sunup.activities.ui.SettingsState
class ViewModelFactory(val application: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = when {
modelClass.isAssignableFrom(MainViewModel::class.java) -> MainViewModel(application)
modelClass.isAssignableFrom(SettingsViewModel::class.java) -> SettingsViewModel(
application
)
modelClass.isAssignableFrom(ThemeViewModel::class.java) -> ThemeViewModel(application)
modelClass.isAssignableFrom(DistribMigrationViewModel::class.java) -> DistribMigrationViewModel(application)
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
} as T
}
class PreviewFactory(val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = when {
modelClass.isAssignableFrom(MainViewModel::class.java) -> {
MainViewModel(
MainUiState(),
BatteryOptimisationViewModel(true),
previewRegistrationsViewModel(context)
)
}
modelClass.isAssignableFrom(SettingsViewModel::class.java) -> {
SettingsViewModel(
SettingsState(
BuildConfig.DEFAULT_API_URL,
false
)
)
}
modelClass.isAssignableFrom(ThemeViewModel::class.java) -> ThemeViewModel()
modelClass.isAssignableFrom(DistribMigrationViewModel::class.java) -> {
DistribMigrationViewModel(DistribMigrationState())
}
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}

View file

@ -1,159 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import android.annotation.SuppressLint
import androidx.annotation.StringRes
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
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 org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.android.distributor.ui.compose.AppBar
import org.unifiedpush.distributor.sunup.R
import org.unifiedpush.distributor.sunup.activities.DistribMigrationViewModel
import org.unifiedpush.distributor.sunup.activities.MainViewModel
import org.unifiedpush.distributor.sunup.activities.PreviewFactory
import org.unifiedpush.distributor.sunup.activities.SettingsViewModel
import org.unifiedpush.distributor.sunup.activities.ThemeViewModel
enum class AppScreen(@StringRes val title: Int) {
Main(R.string.app_name),
Settings(LibR.string.settings)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DefaultTopBar(
currentScreen: AppScreen,
canNavigateBack: Boolean,
navigateUp: () -> Unit,
modifier: Modifier = Modifier
) = AppBar(
currentScreen.title,
canNavigateBack,
navigateUp,
modifier
)
private enum class Dir {
Left,
Right
}
private fun Dir.transform(it: Int): Int = when (this) {
Dir.Left -> it
Dir.Right -> -it
}
private fun slideInTo(dir: Dir): EnterTransition = slideInHorizontally(
animationSpec = tween(durationMillis = 200)
) { dir.transform(it) } + fadeIn(initialAlpha = 1f)
private fun slideOutFrom(dir: Dir): ExitTransition = slideOutHorizontally(
animationSpec = tween(durationMillis = 200)
) { dir.transform(it) } + fadeOut(targetAlpha = 1f)
@Composable
fun App(
factory: ViewModelProvider.Factory,
themeViewModel: ThemeViewModel = viewModel<ThemeViewModel>(factory = factory),
navController: NavHostController = rememberNavController()
) {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentScreen = AppScreen.valueOf(
backStackEntry?.destination?.route ?: AppScreen.Main.name
)
// shared with all views, no need to scope it
val migrationViewModel = viewModel<DistribMigrationViewModel>(factory = factory)
val mainViewModel = viewModel<MainViewModel>(factory = factory)
Scaffold(
topBar = {
when (currentScreen) {
AppScreen.Main -> {
MainAppBarOrSelection(
mainViewModel,
onGoToSettings = {
navController.navigate(AppScreen.Settings.name)
}
)
}
else -> null
} ?: DefaultTopBar(
currentScreen,
canNavigateBack = navController.previousBackStackEntry != null,
navigateUp = { navController.navigateUp() }
)
},
contentWindowInsets = WindowInsets.safeDrawing
) { innerPadding ->
NavHost(
navController = navController,
startDestination = AppScreen.Main.name,
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(innerPadding)
) {
composable(
route = AppScreen.Main.name,
exitTransition = {
when (targetState.destination.route) {
AppScreen.Settings.name -> slideOutFrom(
Dir.Right
)
else -> fadeOut()
}
},
popEnterTransition = {
when (initialState.destination.route) {
AppScreen.Settings.name -> slideInTo(Dir.Right)
else -> fadeIn()
}
}
) {
MainScreen(
mainViewModel,
migrationViewModel
)
}
composable(
route = AppScreen.Settings.name,
enterTransition = { slideInTo(Dir.Left) },
popExitTransition = { slideOutFrom(Dir.Left) }
) {
val vm = viewModel<SettingsViewModel>(factory = factory)
SettingsScreen(vm, themeViewModel, migrationViewModel)
}
}
}
}
@SuppressLint("ViewModelConstructorInComposable")
@Preview
@Composable
fun PreviewApp() = App(
factory = PreviewFactory(LocalContext.current)
)

View file

@ -1,75 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.unifiedpush.android.distributor.ui.R
import org.unifiedpush.distributor.sunup.BuildConfig
@Preview
@Composable
fun ChangeServerUi(currentValue: String = BuildConfig.DEFAULT_API_URL, onValueChange: (String) -> Unit = {}) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text(
stringResource(R.string.push_server)
)
TextField(
value = currentValue,
onValueChange = onValueChange,
label = { Text(stringResource(R.string.url)) },
maxLines = 1
)
}
}
@Preview
@Composable
fun ChangeServerDialog(
currentValue: String = BuildConfig.DEFAULT_API_URL,
onDismissRequest: () -> Unit = {},
onConfirmation: (String) -> Unit = {}
) {
var value by remember { mutableStateOf(currentValue) }
AlertDialog(
title = {
stringResource(R.string.push_server)
},
text = {
ChangeServerUi(value) {
value = it
}
},
onDismissRequest = {
onDismissRequest()
},
confirmButton = {
TextButton(
onClick = {
onConfirmation(value)
}
) {
Text(stringResource(android.R.string.ok))
}
},
dismissButton = {
TextButton(
onClick = {
onDismissRequest()
}
) {
Text(stringResource(android.R.string.cancel))
}
}
)
}

View file

@ -1,230 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.android.distributor.ui.compose.AppBar
import org.unifiedpush.android.distributor.ui.compose.CardDisableBatteryOptimisation
import org.unifiedpush.android.distributor.ui.compose.CardDisabledForMigration
import org.unifiedpush.android.distributor.ui.compose.DistribMigrationUi
import org.unifiedpush.android.distributor.ui.compose.PermissionsUi
import org.unifiedpush.android.distributor.ui.compose.RegistrationList
import org.unifiedpush.android.distributor.ui.compose.RegistrationListHeading
import org.unifiedpush.android.distributor.ui.compose.UnregisterBarUi
import org.unifiedpush.distributor.sunup.EventBus
import org.unifiedpush.distributor.sunup.R
import org.unifiedpush.distributor.sunup.activities.DistribMigrationViewModel
import org.unifiedpush.distributor.sunup.activities.MainViewModel
import org.unifiedpush.distributor.sunup.activities.PreviewFactory
import org.unifiedpush.distributor.sunup.activities.UiAction
import org.unifiedpush.distributor.sunup.utils.getDebugInfo
@Composable
fun MainAppBarOrSelection(viewModel: MainViewModel, onGoToSettings: () -> Unit) {
val registrationsState = viewModel.registrationsViewModel.state
if (registrationsState.selectionCount > 0) {
UnregisterBarUi(
viewModel = viewModel.registrationsViewModel,
onDelete = { viewModel.deleteSelection() }
)
} else {
MainAppBar(
viewModel,
onGoToSettings
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainAppBar(viewModel: MainViewModel, onGoToSettings: () -> Unit) {
var openMenu by remember { mutableStateOf(false) }
AppBar(
R.string.app_name,
false,
{},
actions = {
IconButton(
onClick = {
openMenu = true
}
) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = stringResource(LibR.string.app_bar_dropdown_description)
)
}
Dropdown(
openMenu,
onRestart = {
viewModel.restartService()
openMenu = false
},
onDismiss = {
openMenu = false
},
onGoToSettings = onGoToSettings
)
}
)
}
@Composable
fun Dropdown(
expanded: Boolean,
onRestart: () -> Unit,
onDismiss: () -> Unit,
onGoToSettings: () -> Unit
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismiss
) {
DropdownMenuItem(
onClick = onRestart,
text = {
Text(stringResource(LibR.string.app_dropdown_restart))
}
)
DropdownMenuItem(
onClick = onGoToSettings,
text = {
Text(stringResource(LibR.string.settings))
}
)
}
}
@Composable
fun MainScreen(viewModel: MainViewModel, migrationViewModel: DistribMigrationViewModel) {
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
EventBus.subscribe<UiAction> {
it.handle { type ->
when (type) {
UiAction.Action.RefreshRegistrations -> viewModel.refreshRegistrations()
else -> {}
}
}
}
}
LaunchedEffect(Unit) {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.refreshRegistrations()
}
}
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Spacer(Modifier)
if (migrationViewModel.state.migrated) {
CardDisabledForMigration {
migrationViewModel.reactivateUnifiedPush()
}
return
}
CardDisableBatteryOptimisation(viewModel.batteryOptimisationViewModel)
RegistrationListHeading(
modifier = Modifier.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
viewModel.addDebugClick()
}
)
}
RegistrationList(viewModel.registrationsViewModel) {
// We don't have copyable endpoint
}
}
if (viewModel.mainUiState.showPermissionDialog) {
PermissionsUi {
viewModel.closePermissionDialog()
migrationViewModel.mayShowFallbackIntro()
}
}
if (viewModel.mainUiState.showDebugInfo) {
DebugDialog {
viewModel.dismissDebugInfo()
}
}
if (migrationViewModel.state.showMigrations) {
DistribMigrationUi(migrationViewModel)
}
}
@Composable
fun DebugDialog(onDismissRequest: () -> Unit) {
val text = getDebugInfo()
AlertDialog(
title = { Text("Debug") },
text = {
SelectionContainer {
Text(text)
}
},
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(
onClick = onDismissRequest
) {
Text(stringResource(android.R.string.ok))
}
}
)
}
@Preview
@Composable
fun MainPreview() {
val factory = PreviewFactory(LocalContext.current)
val mainVM = viewModel<MainViewModel>(factory = factory)
val migrationVM = viewModel<DistribMigrationViewModel>(factory = factory)
MainScreen(mainVM, migrationVM)
}

View file

@ -1,3 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
data class MainUiState(val showDebugInfo: Boolean = false, val showPermissionDialog: Boolean = true)

View file

@ -1,56 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withLink
import androidx.compose.ui.tooling.preview.Preview
import org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.distributor.sunup.R
@Composable
fun PrivacyPolicyDialog(onDismiss: () -> Unit) {
AlertDialog(
title = {
Text(stringResource(LibR.string.privacy_policy))
},
text = {
PrivacyPolicy()
},
onDismissRequest = onDismiss,
confirmButton = {
TextButton(
onClick = onDismiss
) {
Text(stringResource(android.R.string.cancel))
}
}
)
}
@Preview
@Composable
private fun PrivacyPolicy() {
Text(
buildAnnotatedString {
append(stringResource(R.string.sunup_privacy_policy).format(stringResource(R.string.app_name)))
withLink(
LinkAnnotation.Url(
"https://www.mozilla.org/en-US/privacy/firefox/#types-of-data-defined",
TextLinkStyles(style = SpanStyle(color = MaterialTheme.colorScheme.primary))
)
) {
append("https://www.mozilla.org/en-US/privacy/firefox/")
}
}
)
}

View file

@ -1,121 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.android.distributor.ui.compose.AboutHeading
import org.unifiedpush.android.distributor.ui.compose.DistribMigrationUi
import org.unifiedpush.android.distributor.ui.compose.DynamicColorsPreferences
import org.unifiedpush.android.distributor.ui.compose.Heading
import org.unifiedpush.android.distributor.ui.compose.MigrationPreferences
import org.unifiedpush.android.distributor.ui.compose.Preference
import org.unifiedpush.android.distributor.ui.compose.ShowToastsPreference
import org.unifiedpush.distributor.sunup.EventBus
import org.unifiedpush.distributor.sunup.R
import org.unifiedpush.distributor.sunup.activities.DistribMigrationViewModel
import org.unifiedpush.distributor.sunup.activities.PreviewFactory
import org.unifiedpush.distributor.sunup.activities.SettingsViewModel
import org.unifiedpush.distributor.sunup.activities.ThemeViewModel
import org.unifiedpush.distributor.sunup.activities.UiAction
@Composable
fun SettingsScreen(
viewModel: SettingsViewModel,
themeViewModel: ThemeViewModel,
migrationViewModel: DistribMigrationViewModel
) {
val state = viewModel.state
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
EventBus.subscribe<UiAction> {
it.handle { type ->
when (type) {
UiAction.Action.RefreshApiUrl -> viewModel.refreshApiUrl()
else -> {}
}
}
}
}
LaunchedEffect(Unit) {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.refreshApiUrl()
migrationViewModel.refreshDistributors()
}
}
Column(
modifier = Modifier
.padding(16.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Heading(R.string.app_name)
Preference(
stringResource(LibR.string.push_server),
stringResource(LibR.string.clicklabel_select_push_server),
state.currentApiUrl,
onSelect = {
viewModel.toggleChangeServer()
}
)
ShowToastsPreference(viewModel.state.showToasts) {
viewModel.toggleShowToasts()
}
DynamicColorsPreferences(themeViewModel.dynamicColors) {
themeViewModel.toggleDynamicColors()
}
MigrationPreferences(migrationViewModel)
AboutHeading()
Preference(
stringResource(LibR.string.privacy_policy),
stringResource(LibR.string.open_privacy_policy_clicklabel),
onSelect = {
viewModel.togglePrivacyPolicy()
}
)
}
if (state.showChangeServerDialog) {
ChangeServerDialog(
state.currentApiUrl,
onDismissRequest = { viewModel.toggleChangeServer() },
onConfirmation = { viewModel.newPushServer(it) }
)
}
if (state.showPrivacyPolicy) {
PrivacyPolicyDialog {
viewModel.togglePrivacyPolicy()
}
}
if (migrationViewModel.state.showMigrations) {
DistribMigrationUi(migrationViewModel)
}
}
@Preview
@Composable
fun PreviewSettingsScreen() {
val factory = PreviewFactory(LocalContext.current)
val settVM = viewModel<SettingsViewModel>(factory = factory)
val themeVM = viewModel<ThemeViewModel>(factory = factory)
val migrationVM = viewModel<DistribMigrationViewModel>(factory = factory)
SettingsScreen(settVM, themeVM, migrationVM)
}

View file

@ -1,21 +0,0 @@
package org.unifiedpush.distributor.sunup.activities.ui
import android.content.Context
import org.unifiedpush.distributor.sunup.AppStore
data class SettingsState(
val currentApiUrl: String,
val showToasts: Boolean,
val showChangeServerDialog: Boolean = false,
val showPrivacyPolicy: Boolean = false
) {
companion object {
fun from(context: Context): SettingsState {
val store = AppStore(context)
return SettingsState(
store.apiUrl,
store.showToasts
)
}
}
}

View file

@ -16,11 +16,12 @@ import okhttp3.WebSocket
import okhttp3.WebSocketListener import okhttp3.WebSocketListener
import org.unifiedpush.android.distributor.ui.R as LibR import org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.distributor.ChannelCreationStatus import org.unifiedpush.distributor.ChannelCreationStatus
import org.unifiedpush.distributor.ipc.ACTION_REFRESH_API_URL
import org.unifiedpush.distributor.ipc.sendUiAction
import org.unifiedpush.distributor.sunup.AppStore import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.DatabaseFactory import org.unifiedpush.distributor.sunup.DatabaseFactory
import org.unifiedpush.distributor.sunup.Distributor import org.unifiedpush.distributor.sunup.Distributor
import org.unifiedpush.distributor.sunup.Distributor.sendMessage import org.unifiedpush.distributor.sunup.Distributor.sendMessage
import org.unifiedpush.distributor.sunup.activities.UiAction
import org.unifiedpush.distributor.sunup.api.data.ClientMessage import org.unifiedpush.distributor.sunup.api.data.ClientMessage
import org.unifiedpush.distributor.sunup.api.data.ServerMessage import org.unifiedpush.distributor.sunup.api.data.ServerMessage
import org.unifiedpush.distributor.sunup.callback.NetworkCallbackFactory import org.unifiedpush.distributor.sunup.callback.NetworkCallbackFactory
@ -202,7 +203,7 @@ class ServerConnection(private val context: Context, private val releaseLock: ()
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
UiAction.publish(UiAction.Action.RefreshApiUrl) sendUiAction(context, ACTION_REFRESH_API_URL)
RestartWorker.run(context, delay = 0) RestartWorker.run(context, delay = 0)
return true return true
} }

View file

@ -0,0 +1,80 @@
package org.unifiedpush.distributor.sunup.services
import android.os.Bundle
import java.text.SimpleDateFormat
import org.unifiedpush.distributor.Database
import org.unifiedpush.distributor.MigrationManager
import org.unifiedpush.distributor.SourceManager
import org.unifiedpush.distributor.UnifiedPushDistributor
import org.unifiedpush.distributor.WorkerCompanion
import org.unifiedpush.distributor.ipc.ACTION_REFRESH_API_URL
import org.unifiedpush.distributor.ipc.handler.IAccount
import org.unifiedpush.distributor.ipc.handler.IApi
import org.unifiedpush.distributor.ipc.sendUiAction
import org.unifiedpush.distributor.service.ForegroundServiceFactory
import org.unifiedpush.distributor.service.InternalService
import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.BuildConfig
import org.unifiedpush.distributor.sunup.DatabaseFactory
import org.unifiedpush.distributor.sunup.Distributor
import org.unifiedpush.distributor.sunup.api.ApiUrlCandidate
import org.unifiedpush.distributor.sunup.api.ServerConnection
class InternalServiceImpl : InternalService() {
override val sourceManager: SourceManager<*>
get() = SourceManager
override val restartWorker: WorkerCompanion
get() = RestartWorker.Companion
override val startService: ForegroundServiceFactory
get() = FgService.Companion
override val migrationManager: MigrationManager
get() = MigrationManagerImpl()
override val distributor: UnifiedPushDistributor
get() = Distributor
override val db: Database
get() = DatabaseFactory.getDb(this)
override fun getDebugInfo(): String {
val date = ServerConnection.lastEventDate?.let {
SimpleDateFormat.getDateTimeInstance().format(it.time)
} ?: "None"
return "ServiceStarted: ${FgService.isServiceStarted()}\n" +
"Last Event: $date\n" +
org.unifiedpush.distributor.sunup.services.SourceManager.getDebugInfo()
}
/**
* Not used by Sunup
*/
override fun account() = object : IAccount {
override fun get(): String? = null
override fun logout() {}
override fun login(data: Bundle) {}
}
override fun api() = object : IApi {
override fun newPushServer(url: String?) {
url?.let {
ApiUrlCandidate.test(context, url)
} ?: run {
AppStore(context).apiUrl = BuildConfig.DEFAULT_API_URL
restartWorker().restart()
sendUiAction(context, ACTION_REFRESH_API_URL)
}
}
override fun getUrl() = AppStore(context).apiUrl
}
override var themeDynamicColors: Boolean
get() = AppStore(context).dynamicColors
set(value) {
AppStore(context).dynamicColors = value
}
override var showToasts: Boolean
get() = AppStore(context).showToasts
set(value) {
AppStore(context).showToasts = value
}
}

View file

@ -3,8 +3,8 @@ package org.unifiedpush.distributor.sunup.services
import android.content.Context import android.content.Context
import org.unifiedpush.distributor.Database import org.unifiedpush.distributor.Database
import org.unifiedpush.distributor.RegistrationCounter import org.unifiedpush.distributor.RegistrationCounter
import org.unifiedpush.distributor.ipc.sendUiAction
import org.unifiedpush.distributor.sunup.DatabaseFactory import org.unifiedpush.distributor.sunup.DatabaseFactory
import org.unifiedpush.distributor.sunup.activities.UiAction
import org.unifiedpush.distributor.sunup.utils.ForegroundNotification import org.unifiedpush.distributor.sunup.utils.ForegroundNotification
object MainRegistrationCounter : RegistrationCounter() { object MainRegistrationCounter : RegistrationCounter() {
@ -15,7 +15,7 @@ object MainRegistrationCounter : RegistrationCounter() {
override fun onCountRefreshed(context: Context) { override fun onCountRefreshed(context: Context) {
ForegroundNotification(context).update() ForegroundNotification(context).update()
UiAction.publish(UiAction.Action.RefreshRegistrations) sendUiAction(context, "REFRESH_REGISTRATIONS")
} }
override fun getDb(context: Context): Database = DatabaseFactory.getDb(context) override fun getDb(context: Context): Database = DatabaseFactory.getDb(context)

View file

@ -1,11 +1,11 @@
package org.unifiedpush.distributor.sunup.services package org.unifiedpush.distributor.sunup.services
import android.content.Context import android.content.Context
import org.unifiedpush.distributor.MigrationManager as MManager import org.unifiedpush.distributor.MigrationManager
import org.unifiedpush.distributor.sunup.AppStore import org.unifiedpush.distributor.sunup.AppStore
import org.unifiedpush.distributor.sunup.Distributor import org.unifiedpush.distributor.sunup.Distributor
class MigrationManager : MManager() { class MigrationManagerImpl : MigrationManager() {
override val distrib = Distributor override val distrib = Distributor
override fun getStore(context: Context): MigrationStore = AppStore(context) override fun getStore(context: Context): MigrationStore = AppStore(context)
} }

View file

@ -10,7 +10,7 @@ import org.unifiedpush.distributor.sunup.utils.DisconnectedNotification
object SourceManager : SManager<WebSocket>() { object SourceManager : SManager<WebSocket>() {
override val foregroundService = FgService.service override val foregroundService = FgService.service
override val migrationManager = MigrationManager() override val migrationManager = MigrationManagerImpl()
override fun disconnectedNotification(context: Context): AppNotification = DisconnectedNotification(context) override fun disconnectedNotification(context: Context): AppNotification = DisconnectedNotification(context)

View file

@ -1,15 +0,0 @@
package org.unifiedpush.distributor.sunup.utils
import java.text.SimpleDateFormat
import org.unifiedpush.distributor.sunup.api.ServerConnection
import org.unifiedpush.distributor.sunup.services.FgService
import org.unifiedpush.distributor.sunup.services.SourceManager
fun getDebugInfo(): String {
val date = ServerConnection.lastEventDate?.let {
SimpleDateFormat.getDateTimeInstance().format(it.time)
} ?: "None"
return "ServiceStarted: ${FgService.isServiceStarted()}\n" +
"Last Event: $date\n" +
SourceManager.getDebugInfo()
}

View file

@ -7,7 +7,6 @@ import java.util.concurrent.atomic.AtomicBoolean
import org.unifiedpush.android.distributor.ui.R as LibR import org.unifiedpush.android.distributor.ui.R as LibR
import org.unifiedpush.distributor.AppNotification import org.unifiedpush.distributor.AppNotification
import org.unifiedpush.distributor.sunup.R import org.unifiedpush.distributor.sunup.R
import org.unifiedpush.distributor.sunup.activities.MainActivity
import org.unifiedpush.distributor.sunup.services.MainRegistrationCounter import org.unifiedpush.distributor.sunup.services.MainRegistrationCounter
const val NOTIFICATION_ID_FOREGROUND = 51115 const val NOTIFICATION_ID_FOREGROUND = 51115
@ -25,8 +24,7 @@ class MainNotificationData(
text = text, text = text,
ticker = ticker, ticker = ticker,
priority = priority, priority = priority,
ongoing = ongoing, ongoing = ongoing
activity = MainActivity::class.java
) )
private val Context.warningChannelData: AppNotification.ChannelData private val Context.warningChannelData: AppNotification.ChannelData