mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-10-15 15:59:18 +00:00
Add battery saver warning (#542)
* Show battery saver warning in account list and debug info * Move app warnings to model class * Debug info: more verbose text * Restore previous strings for sync enqueued/started
This commit is contained in:
parent
fbed5c7d67
commit
dd036b91fc
|
@ -14,6 +14,7 @@ import android.content.pm.ShortcutManager
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
|
@ -49,7 +50,12 @@ import androidx.compose.material.TopAppBar
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.BatterySaver
|
||||
import androidx.compose.material.icons.filled.DataSaverOn
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.NotificationsOff
|
||||
import androidx.compose.material.icons.filled.SignalCellularOff
|
||||
import androidx.compose.material.icons.filled.Storage
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
|
@ -82,6 +88,7 @@ import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
|
|||
import at.bitfire.davdroid.syncadapter.SyncUtils
|
||||
import at.bitfire.davdroid.syncadapter.SyncWorker
|
||||
import at.bitfire.davdroid.ui.account.AccountActivity
|
||||
import at.bitfire.davdroid.ui.account.AppWarningsModel
|
||||
import at.bitfire.davdroid.ui.intro.IntroActivity
|
||||
import at.bitfire.davdroid.ui.setup.LoginActivity
|
||||
import at.bitfire.davdroid.ui.widget.ActionCard
|
||||
|
@ -105,6 +112,7 @@ class AccountsActivity: AppCompatActivity() {
|
|||
@Inject lateinit var accountsDrawerHandler: AccountsDrawerHandler
|
||||
|
||||
private val model by viewModels<Model>()
|
||||
private val warnings by viewModels<AppWarningsModel>()
|
||||
|
||||
private val introActivityLauncher = registerForActivityResult(IntroActivity.Contract) { cancelled ->
|
||||
if (cancelled)
|
||||
|
@ -165,8 +173,6 @@ class AccountsActivity: AppCompatActivity() {
|
|||
)
|
||||
|
||||
Column {
|
||||
val warnings = model.warnings
|
||||
|
||||
val notificationsPermissionState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
rememberPermissionState(
|
||||
permission = Manifest.permission.POST_NOTIFICATIONS
|
||||
|
@ -183,13 +189,7 @@ class AccountsActivity: AppCompatActivity() {
|
|||
},
|
||||
internetWarning = warnings.networkAvailable.observeAsState().value == false,
|
||||
onManageConnections = {
|
||||
val intent = Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
},
|
||||
lowStorageWarning = warnings.storageLow.observeAsState().value == true,
|
||||
onManageStorage = {
|
||||
val intent = Intent(android.provider.Settings.ACTION_INTERNAL_STORAGE_SETTINGS)
|
||||
val intent = Intent(Settings.ACTION_WIRELESS_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
},
|
||||
|
@ -198,6 +198,18 @@ class AccountsActivity: AppCompatActivity() {
|
|||
val intent = Intent(android.provider.Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS, Uri.parse("package:" + packageName))
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
},
|
||||
batterySaverActive = warnings.batterySaverActive.observeAsState().value == true,
|
||||
onManageBatterySaver = {
|
||||
val intent = Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
},
|
||||
lowStorageWarning = warnings.storageLow.observeAsState().value == true,
|
||||
onManageStorage = {
|
||||
val intent = Intent(android.provider.Settings.ACTION_INTERNAL_STORAGE_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -244,12 +256,19 @@ class AccountsActivity: AppCompatActivity() {
|
|||
scope: CoroutineScope
|
||||
): @Composable (SnackbarHostState) -> Unit = {
|
||||
SnackbarHost(snackbarHostState)
|
||||
model.feedback.observeAsState().value?.let { msg ->
|
||||
model.syncEnqueued.observeAsState().value?.let { enqueued ->
|
||||
if (enqueued)
|
||||
scope.launch {
|
||||
val msg = getString(
|
||||
if (warnings.networkAvailable.value == true)
|
||||
R.string.sync_started
|
||||
else
|
||||
R.string.no_internet_sync_scheduled
|
||||
)
|
||||
snackbarHostState.showSnackbar(msg)
|
||||
}
|
||||
// reset feedback
|
||||
model.feedback.value = null
|
||||
model.syncEnqueued.value = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,11 +362,10 @@ class AccountsActivity: AppCompatActivity() {
|
|||
@HiltViewModel
|
||||
class Model @Inject constructor(
|
||||
application: Application,
|
||||
val db: AppDatabase,
|
||||
val warnings: AppWarningsManager
|
||||
val db: AppDatabase
|
||||
): AndroidViewModel(application), OnAccountsUpdateListener {
|
||||
|
||||
val feedback = MutableLiveData<String>()
|
||||
val syncEnqueued = MutableLiveData<Boolean>()
|
||||
|
||||
val accountManager = AccountManager.get(application)
|
||||
private val accountType = application.getString(R.string.account_type)
|
||||
|
@ -394,8 +412,6 @@ class AccountsActivity: AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
val networkAvailable = warnings.networkAvailable
|
||||
|
||||
val showAddAccount = MutableLiveData(true)
|
||||
|
||||
init {
|
||||
|
@ -416,12 +432,7 @@ class AccountsActivity: AppCompatActivity() {
|
|||
if (Build.VERSION.SDK_INT >= 25)
|
||||
context.getSystemService<ShortcutManager>()?.reportShortcutUsed(UiUtils.SHORTCUT_SYNC_ALL)
|
||||
|
||||
feedback.value = context.getString(
|
||||
if (networkAvailable.value == false)
|
||||
R.string.no_internet_sync_scheduled
|
||||
else
|
||||
R.string.sync_started
|
||||
)
|
||||
syncEnqueued.value = true
|
||||
|
||||
// Enqueue sync worker for all accounts and authorities. Will sync once internet is available
|
||||
for (account in allAccounts())
|
||||
|
@ -539,15 +550,17 @@ fun SyncWarnings(
|
|||
onClickPermissions: () -> Unit = {},
|
||||
internetWarning: Boolean,
|
||||
onManageConnections: () -> Unit = {},
|
||||
lowStorageWarning: Boolean,
|
||||
onManageStorage: () -> Unit = {},
|
||||
batterySaverActive: Boolean,
|
||||
onManageBatterySaver: () -> Unit = {},
|
||||
dataSaverActive: Boolean,
|
||||
onManageDataSaver: () -> Unit = {}
|
||||
onManageDataSaver: () -> Unit = {},
|
||||
lowStorageWarning: Boolean,
|
||||
onManageStorage: () -> Unit = {}
|
||||
) {
|
||||
Column(Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
|
||||
if (notificationsWarning)
|
||||
ActionCard(
|
||||
icon = painterResource(R.drawable.ic_notifications_off),
|
||||
icon = Icons.Default.NotificationsOff,
|
||||
actionText = stringResource(R.string.account_permissions_action),
|
||||
onAction = onClickPermissions
|
||||
) {
|
||||
|
@ -556,30 +569,39 @@ fun SyncWarnings(
|
|||
|
||||
if (internetWarning)
|
||||
ActionCard(
|
||||
icon = painterResource(R.drawable.ic_signal_cellular_off),
|
||||
icon = Icons.Default.SignalCellularOff,
|
||||
actionText = stringResource(R.string.account_list_manage_connections),
|
||||
onAction = onManageConnections
|
||||
) {
|
||||
Text(stringResource(R.string.account_list_no_internet))
|
||||
}
|
||||
|
||||
if (lowStorageWarning)
|
||||
if (batterySaverActive)
|
||||
ActionCard(
|
||||
icon = painterResource(R.drawable.ic_storage),
|
||||
actionText = stringResource(R.string.account_list_manage_storage),
|
||||
onAction = onManageStorage
|
||||
icon = Icons.Default.BatterySaver,
|
||||
actionText = stringResource(R.string.account_list_manage_battery_saver),
|
||||
onAction = onManageBatterySaver
|
||||
) {
|
||||
Text(stringResource(R.string.account_list_low_storage))
|
||||
Text(stringResource(R.string.account_list_battery_saver_enabled))
|
||||
}
|
||||
|
||||
if (dataSaverActive)
|
||||
ActionCard(
|
||||
icon = painterResource(R.drawable.ic_datasaver_on),
|
||||
icon = Icons.Default.DataSaverOn,
|
||||
actionText = stringResource(R.string.account_list_manage_datasaver),
|
||||
onAction = onManageDataSaver
|
||||
) {
|
||||
Text(stringResource(R.string.account_list_datasaver_enabled))
|
||||
}
|
||||
|
||||
if (lowStorageWarning)
|
||||
ActionCard(
|
||||
icon = Icons.Default.Storage,
|
||||
actionText = stringResource(R.string.account_list_manage_storage),
|
||||
onAction = onManageStorage
|
||||
) {
|
||||
Text(stringResource(R.string.account_list_low_storage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,7 +611,8 @@ fun SyncWarnings_Preview() {
|
|||
SyncWarnings(
|
||||
notificationsWarning = true,
|
||||
internetWarning = true,
|
||||
lowStorageWarning = true,
|
||||
dataSaverActive = true
|
||||
batterySaverActive = true,
|
||||
dataSaverActive = true,
|
||||
lowStorageWarning = true
|
||||
)
|
||||
}
|
|
@ -570,6 +570,38 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
.append(FileUtils.byteCountToDisplaySize(statFs.totalBytes))
|
||||
.append("\n\n")
|
||||
|
||||
// power saving
|
||||
if (Build.VERSION.SDK_INT >= 28)
|
||||
context.getSystemService<UsageStatsManager>()?.let { statsManager ->
|
||||
val bucket = statsManager.appStandbyBucket
|
||||
writer
|
||||
.append("App standby bucket: ")
|
||||
.append(
|
||||
when {
|
||||
bucket <= 5 -> "exempted (very good)"
|
||||
bucket <= UsageStatsManager.STANDBY_BUCKET_ACTIVE -> "active (good)"
|
||||
bucket <= UsageStatsManager.STANDBY_BUCKET_WORKING_SET -> "working set (bad: job restrictions apply)"
|
||||
bucket <= UsageStatsManager.STANDBY_BUCKET_FREQUENT -> "frequent (bad: job restrictions apply)"
|
||||
bucket <= UsageStatsManager.STANDBY_BUCKET_RARE -> "rare (very bad: job and network restrictions apply)"
|
||||
bucket <= UsageStatsManager.STANDBY_BUCKET_RESTRICTED -> "restricted (very bad: job and network restrictions apply)"
|
||||
else -> "$bucket"
|
||||
}
|
||||
)
|
||||
writer.append('\n')
|
||||
}
|
||||
context.getSystemService<PowerManager>()?.let { powerManager ->
|
||||
writer.append("App exempted from power saving: ")
|
||||
.append(if (powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) "yes (good)" else "no (bad)")
|
||||
.append('\n')
|
||||
.append("System in power-save mode: ")
|
||||
.append(if (powerManager.isPowerSaveMode) "yes (restrictions apply!)" else "no")
|
||||
.append('\n')
|
||||
}
|
||||
// system-wide sync
|
||||
writer.append("System-wide synchronization: ")
|
||||
.append(if (ContentResolver.getMasterSyncAutomatically()) "automatically" else "manually")
|
||||
.append("\n\n")
|
||||
|
||||
// connectivity
|
||||
context.getSystemService<ConnectivityManager>()?.let { connectivityManager ->
|
||||
writer.append("\nCONNECTIVITY\n\n")
|
||||
|
@ -608,24 +640,6 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
writer.append("\nCONFIGURATION\n\n")
|
||||
// power saving
|
||||
if (Build.VERSION.SDK_INT >= 28)
|
||||
context.getSystemService<UsageStatsManager>()?.let { statsManager ->
|
||||
val bucket = statsManager.appStandbyBucket
|
||||
writer.append("App standby bucket: $bucket")
|
||||
if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE)
|
||||
writer.append(" (RESTRICTED!)")
|
||||
writer.append('\n')
|
||||
}
|
||||
context.getSystemService<PowerManager>()?.let { powerManager ->
|
||||
writer.append("Power saving disabled: ")
|
||||
.append(if (powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) "yes" else "no")
|
||||
.append('\n')
|
||||
}
|
||||
// system-wide sync
|
||||
writer.append("System-wide synchronization: ")
|
||||
.append(if (ContentResolver.getMasterSyncAutomatically()) "automatically" else "manually")
|
||||
.append('\n')
|
||||
// notifications
|
||||
val nm = NotificationManagerCompat.from(context)
|
||||
writer.append("\nNotifications")
|
||||
|
|
|
@ -20,7 +20,11 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.ActivityAccountBinding
|
||||
|
@ -30,7 +34,6 @@ import at.bitfire.davdroid.db.Service
|
|||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.syncadapter.SyncWorker
|
||||
import at.bitfire.davdroid.ui.AppWarningsManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
|
@ -38,7 +41,9 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -60,6 +65,8 @@ class AccountActivity: AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private val warningsModel by viewModels<AppWarningsModel>()
|
||||
|
||||
private lateinit var binding: ActivityAccountBinding
|
||||
|
||||
|
||||
|
@ -94,13 +101,17 @@ class AccountActivity: AppCompatActivity() {
|
|||
|
||||
// "Sync now" fab
|
||||
TooltipCompat.setTooltipText(binding.sync, binding.sync.contentDescription)
|
||||
model.networkAvailable.observe(this) { networkAvailable ->
|
||||
warningsModel.networkAvailable.observe(this) { networkAvailable ->
|
||||
binding.sync.setOnClickListener {
|
||||
if (!networkAvailable)
|
||||
val msgId =
|
||||
if (warningsModel.networkAvailable.value == true)
|
||||
R.string.sync_started
|
||||
else
|
||||
R.string.no_internet_sync_scheduled
|
||||
Snackbar.make(
|
||||
binding.sync,
|
||||
R.string.no_internet_sync_scheduled,
|
||||
Snackbar.LENGTH_LONG
|
||||
msgId,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
SyncWorker.enqueueAllAuthorities(this, model.account)
|
||||
}
|
||||
|
@ -276,8 +287,7 @@ class AccountActivity: AppCompatActivity() {
|
|||
class Model @AssistedInject constructor(
|
||||
application: Application,
|
||||
val db: AppDatabase,
|
||||
@Assisted val account: Account,
|
||||
warnings: AppWarningsManager
|
||||
@Assisted val account: Account
|
||||
): AndroidViewModel(application), OnAccountsUpdateListener {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -294,8 +304,6 @@ class AccountActivity: AppCompatActivity() {
|
|||
val showOnlyPersonal = MutableLiveData<Boolean>()
|
||||
val showOnlyPersonalWritable = MutableLiveData<Boolean>()
|
||||
|
||||
val networkAvailable = warnings.networkAvailable
|
||||
|
||||
|
||||
init {
|
||||
accountManager.addOnAccountsUpdatedListener(this, null, true)
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
||||
**************************************************************************************************/
|
||||
|
||||
package at.bitfire.davdroid.ui
|
||||
package at.bitfire.davdroid.ui.account
|
||||
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
|
@ -14,12 +15,12 @@ import android.net.ConnectivityManager
|
|||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import at.bitfire.davdroid.StorageLowReceiver
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -34,31 +35,32 @@ import javax.inject.Inject
|
|||
* - whether a network connection is available → [networkAvailable]
|
||||
* - whether data saver is turned on -> [dataSaverEnabled]
|
||||
*/
|
||||
class AppWarningsManager @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
@HiltViewModel
|
||||
class AppWarningsModel @Inject constructor(
|
||||
context: Application,
|
||||
storageLowReceiver: StorageLowReceiver
|
||||
) : AutoCloseable, SyncStatusObserver {
|
||||
): AndroidViewModel(context), SyncStatusObserver {
|
||||
|
||||
/** whether storage is low (prevents sync framework from running synchronization) */
|
||||
val storageLow = storageLowReceiver.storageLow
|
||||
|
||||
/** whether global sync is disabled (sync framework won't run automatic synchronization in this case) */
|
||||
val globalSyncDisabled = MutableLiveData(false)
|
||||
val globalSyncDisabled = MutableLiveData<Boolean>()
|
||||
private var syncStatusObserver: Any? = null
|
||||
|
||||
/** whether a usable network connection is available (sync framework won't run synchronization otherwise) */
|
||||
val networkAvailable = MutableLiveData<Boolean>()
|
||||
private var networkCallback: ConnectivityManager.NetworkCallback? = null
|
||||
private var networkReceiver: BroadcastReceiver? = null
|
||||
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
|
||||
private val connectivityManager = context.getSystemService<ConnectivityManager>()!!
|
||||
|
||||
val batterySaverActive = MutableLiveData<Boolean>()
|
||||
private val batterySaverListener: BroadcastReceiver
|
||||
|
||||
/** whether data saver is restricting background synchronization ([ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED]) */
|
||||
val dataSaverEnabled = MutableLiveData<Boolean>()
|
||||
var dataSaverChangedListener: BroadcastReceiver? = null
|
||||
private val dataSaverChangedListener: BroadcastReceiver
|
||||
|
||||
init {
|
||||
Logger.log.fine("Watching for warning conditions")
|
||||
|
||||
// Automatic Sync
|
||||
syncStatusObserver = ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this)
|
||||
onStatusChanged(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS)
|
||||
|
@ -66,19 +68,57 @@ class AppWarningsManager @Inject constructor(
|
|||
// Network
|
||||
watchConnectivity()
|
||||
|
||||
// Battery saver
|
||||
batterySaverListener = object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
checkBatterySaver()
|
||||
}
|
||||
}
|
||||
val batterySaverListenerFilter = IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
|
||||
context.registerReceiver(batterySaverListener, batterySaverListenerFilter)
|
||||
checkBatterySaver()
|
||||
|
||||
// Data saver
|
||||
val listener = object: BroadcastReceiver() {
|
||||
dataSaverChangedListener = object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
checkDataSaver()
|
||||
}
|
||||
}
|
||||
|
||||
val dataSaverChangedFilter = IntentFilter(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED)
|
||||
context.registerReceiver(listener, dataSaverChangedFilter)
|
||||
dataSaverChangedListener = listener
|
||||
context.registerReceiver(dataSaverChangedListener, dataSaverChangedFilter)
|
||||
checkDataSaver()
|
||||
}
|
||||
|
||||
private fun checkBatterySaver() {
|
||||
batterySaverActive.postValue(
|
||||
getApplication<Application>().getSystemService<PowerManager>()?.isPowerSaveMode
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkDataSaver() {
|
||||
dataSaverEnabled.postValue(
|
||||
getApplication<Application>().getSystemService<ConnectivityManager>()?.let { connectivityManager ->
|
||||
connectivityManager.restrictBackgroundStatus == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
val context = getApplication<Application>()
|
||||
|
||||
// Automatic sync
|
||||
ContentResolver.removeStatusChangeListener(syncStatusObserver)
|
||||
|
||||
// Network
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback)
|
||||
|
||||
// Battery saver
|
||||
context.unregisterReceiver(batterySaverListener)
|
||||
|
||||
// Data Saver
|
||||
context.unregisterReceiver(dataSaverChangedListener)
|
||||
}
|
||||
|
||||
override fun onStatusChanged(which: Int) {
|
||||
globalSyncDisabled.postValue(!ContentResolver.getMasterSyncAutomatically())
|
||||
}
|
||||
|
@ -91,7 +131,7 @@ class AppWarningsManager @Inject constructor(
|
|||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
.build()
|
||||
val callback = object: ConnectivityManager.NetworkCallback() {
|
||||
networkCallback = object: ConnectivityManager.NetworkCallback() {
|
||||
val availableNetworks = hashSetOf<Network>()
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
|
@ -108,37 +148,7 @@ class AppWarningsManager @Inject constructor(
|
|||
networkAvailable.postValue(availableNetworks.isNotEmpty())
|
||||
}
|
||||
}
|
||||
connectivityManager.registerNetworkCallback(networkRequest, callback)
|
||||
networkCallback = callback
|
||||
}
|
||||
|
||||
private fun checkDataSaver() {
|
||||
dataSaverEnabled.postValue(
|
||||
context.getSystemService<ConnectivityManager>()?.let { connectivityManager ->
|
||||
connectivityManager.restrictBackgroundStatus == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
Logger.log.fine("Stopping watching for warning conditions")
|
||||
|
||||
// Automatic sync
|
||||
ContentResolver.removeStatusChangeListener(syncStatusObserver)
|
||||
|
||||
// Network
|
||||
networkReceiver?.let {
|
||||
context.unregisterReceiver(it)
|
||||
}
|
||||
networkCallback?.let {
|
||||
connectivityManager.unregisterNetworkCallback(it)
|
||||
}
|
||||
|
||||
// Data Saver
|
||||
dataSaverChangedListener?.let { listener ->
|
||||
context.unregisterReceiver(listener)
|
||||
dataSaverChangedListener = null
|
||||
}
|
||||
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
|
||||
}
|
||||
|
||||
}
|
|
@ -13,10 +13,12 @@ import androidx.compose.material.Icon
|
|||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.NotificationAdd
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -24,7 +26,7 @@ import at.bitfire.davdroid.R
|
|||
|
||||
@Composable
|
||||
fun ActionCard(
|
||||
icon: Painter? = null,
|
||||
icon: ImageVector? = null,
|
||||
actionText: String? = null,
|
||||
onAction: () -> Unit = {},
|
||||
content: @Composable () -> Unit
|
||||
|
@ -59,7 +61,7 @@ fun ActionCard(
|
|||
@Preview
|
||||
fun ActionCard_Sample() {
|
||||
ActionCard(
|
||||
icon = painterResource(R.drawable.ic_notifications_off),
|
||||
icon = Icons.Default.NotificationAdd,
|
||||
actionText = "Some Action"
|
||||
) {
|
||||
Text("Some Content")
|
||||
|
|
|
@ -145,10 +145,12 @@
|
|||
<string name="account_list_no_notification_permission">Notifications disabled. You won\'t be notified about sync errors.</string>
|
||||
<string name="account_list_no_internet">No validated Internet connectivity. Synchronization may not run.</string>
|
||||
<string name="account_list_manage_connections">Manage connections</string>
|
||||
<string name="account_list_low_storage">Storage space low. Android will not sync local changes immediately, but during the next regular sync.</string>
|
||||
<string name="account_list_manage_storage">Manage storage</string>
|
||||
<string name="account_list_datasaver_enabled">Data saver enabled. Background synchronization is restricted.</string>
|
||||
<string name="account_list_manage_datasaver">Manage data saver</string>
|
||||
<string name="account_list_battery_saver_enabled">Battery saver enabled. Synchronization may be restricted.</string>
|
||||
<string name="account_list_manage_battery_saver">Manage battery saver</string>
|
||||
<string name="account_list_low_storage">Storage space low. Android will not sync local changes immediately, but during the next regular sync.</string>
|
||||
<string name="account_list_manage_storage">Manage storage</string>
|
||||
<string name="account_list_empty">Welcome to DAVx⁵!\n\nYou can add a CalDAV/CardDAV account now.</string>
|
||||
<string name="accounts_global_sync_disabled">System-wide automatic synchronization is disabled</string>
|
||||
<string name="accounts_global_sync_enable">Enable</string>
|
||||
|
|
Loading…
Reference in a new issue