Skip to content

Commit

Permalink
Show a warning if calendar or contacts storage is deactivated or miss…
Browse files Browse the repository at this point in the history
…ing (#1243)

* Add AppTheme to previews

* Show warning when contacts or calendar system apps are missing or disabled

* Change android icon to database missing icon

* Remove duplication

* Use packageChangedFlow to observer live changes

* Send user to settings app when deactivated and manual when missing

* Find whether content provider app is available by authority

* [WIP] Minor changes

* Open "Manage apps" instead of manual

---------

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
  • Loading branch information
sunkup and rfc2822 authored Jan 24, 2025
1 parent 2438f1a commit 2d686be
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 31 deletions.
31 changes: 29 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import android.accounts.Account
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager.NameNotFoundException
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.PowerManager
import android.provider.CalendarContract
import android.provider.ContactsContract
import androidx.core.content.getSystemService
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.lifecycle.ViewModel
Expand All @@ -30,6 +33,7 @@ import at.bitfire.davdroid.ui.account.AccountProgress
import at.bitfire.davdroid.ui.intro.IntroPage
import at.bitfire.davdroid.ui.intro.IntroPageFactory
import at.bitfire.davdroid.util.broadcastReceiverFlow
import at.bitfire.davdroid.util.packageChangedFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
Expand All @@ -48,9 +52,9 @@ import java.util.logging.Logger

@HiltViewModel(assistedFactory = AccountsModel.Factory::class)
class AccountsModel @AssistedInject constructor(
@Assisted val syncAccountsOnInit: Boolean,
@Assisted private val syncAccountsOnInit: Boolean,
private val accountRepository: AccountRepository,
@ApplicationContext val context: Context,
@ApplicationContext private val context: Context,
private val db: AppDatabase,
introPageFactory: IntroPageFactory,
private val logger: Logger,
Expand Down Expand Up @@ -203,6 +207,16 @@ class AccountsModel @AssistedInject constructor(
}
}

/** whether the calendar storage is missing or disabled */
val calendarStorageDisabled = packageChangedFlow(context).map {
!contentProviderAvailable(CalendarContract.AUTHORITY)
}

/** whether the calendar storage is missing or disabled */
val contactsStorageDisabled = packageChangedFlow(context).map {
!contentProviderAvailable(ContactsContract.AUTHORITY)
}


init {
if (syncAccountsOnInit)
Expand All @@ -221,4 +235,17 @@ class AccountsModel @AssistedInject constructor(
syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true)
}


// helpers

fun contentProviderAvailable(authority: String): Boolean =
try {
// resolveContentProvider returns null if the provider app is disabled or missing;
// so we can't distinguish between "disabled" and "not found"
context.packageManager.resolveContentProvider(authority, 0) != null
} catch (_: NameNotFoundException) {
logger.fine("$authority provider app not found")
false
}

}
104 changes: 75 additions & 29 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -122,7 +124,9 @@ fun AccountsScreen(
internetUnavailable = !model.networkAvailable.collectAsStateWithLifecycle(false).value,
batterySaverActive = model.batterySaverActive.collectAsStateWithLifecycle(false).value,
dataSaverActive = model.dataSaverEnabled.collectAsStateWithLifecycle(false).value,
storageLow = model.storageLow.collectAsStateWithLifecycle(false).value
storageLow = model.storageLow.collectAsStateWithLifecycle(false).value,
calendarStorageDisabled = model.calendarStorageDisabled.collectAsStateWithLifecycle(false).value,
contactsStorageDisabled = model.contactsStorageDisabled.collectAsStateWithLifecycle(false).value
)
}

Expand All @@ -140,7 +144,9 @@ fun AccountsScreen(
internetUnavailable: Boolean = false,
batterySaverActive: Boolean = false,
dataSaverActive: Boolean = false,
storageLow: Boolean = false
storageLow: Boolean = false,
calendarStorageDisabled: Boolean = false,
contactsStorageDisabled: Boolean = false
) {
val scope = rememberCoroutineScope()

Expand Down Expand Up @@ -293,7 +299,14 @@ fun AccountsScreen(
val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS)
if (intent.resolveActivity(context.packageManager) != null)
context.startActivity(intent)
}
},
calendarStorageDisabled = calendarStorageDisabled,
contactsStorageDisabled = contactsStorageDisabled,
onManageApps = {
val intent = Intent(Settings.ACTION_APPLICATION_SETTINGS)
if (intent.resolveActivity(context.packageManager) != null)
context.startActivity(intent)
},
)

// account list
Expand Down Expand Up @@ -435,36 +448,42 @@ fun AccountList(
@Composable
@Preview
fun AccountList_Preview_Idle() {
AccountList(
listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Idle
AppTheme {
AccountList(
listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Idle
)
)
)
)
}
}

@Composable
@Preview
fun AccountList_Preview_SyncPending() {
AccountList(listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Pending
)
))
AppTheme {
AccountList(listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Pending
)
))
}
}

@Composable
@Preview
fun AccountList_Preview_Syncing() {
AccountList(listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Active
)
))
AppTheme {
AccountList(listOf(
AccountsModel.AccountInfo(
Account("Account Name", "test"),
AccountProgress.Active
)
))
}
}


Expand All @@ -479,7 +498,10 @@ fun SyncWarnings(
dataSaverActive: Boolean = true,
onManageDataSaver: () -> Unit = {},
lowStorageWarning: Boolean = true,
onManageStorage: () -> Unit = {}
onManageStorage: () -> Unit = {},
calendarStorageDisabled: Boolean = false,
contactsStorageDisabled: Boolean = false,
onManageApps: () -> Unit = {}
) {
Column(Modifier.padding(horizontal = 8.dp)) {
if (notificationsWarning)
Expand Down Expand Up @@ -531,17 +553,41 @@ fun SyncWarnings(
) {
Text(stringResource(R.string.account_list_low_storage))
}

if (calendarStorageDisabled)
ActionCard(
icon = ImageVector.vectorResource(R.drawable.ic_database_off),
actionText = stringResource(R.string.account_list_manage_apps),
onAction = onManageApps,
modifier = Modifier.padding(vertical = 4.dp)
) {
Text(stringResource(R.string.account_list_calendar_storage_disabled))
}

if (contactsStorageDisabled)
ActionCard(
icon = ImageVector.vectorResource(R.drawable.ic_database_off),
actionText = stringResource(R.string.account_list_manage_apps),
onAction = onManageApps,
modifier = Modifier.padding(vertical = 4.dp)
) {
Text(stringResource(R.string.account_list_contacts_storage_disabled))
}
}
}

@Composable
@Preview
fun SyncWarnings_Preview() {
SyncWarnings(
notificationsWarning = true,
internetWarning = true,
batterySaverActive = true,
dataSaverActive = true,
lowStorageWarning = true
)
AppTheme {
SyncWarnings(
notificationsWarning = true,
internetWarning = true,
batterySaverActive = true,
dataSaverActive = true,
lowStorageWarning = true,
calendarStorageDisabled = true,
contactsStorageDisabled = true
)
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_database_off.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M446,514ZM552,419ZM446,514ZM552,419ZM446,514ZM552,419ZM791,904 L56,169l56,-57 736,736 -57,56ZM480,840q-151,0 -255.5,-46.5T120,680v-400q0,-26 17.5,-49.5T187,187l252,252q-72,-3 -133,-18t-106,-40v120q51,29 123,44t157,15q20,0 39,-0.5t38,-2.5l70,70q-34,7 -71,10t-76,3q-85,0 -157,-15t-123,-44v99q9,29 97.5,54.5T480,760q64,0 128.5,-13T715,715l58,58q-49,31 -125.5,49T480,840ZM830,717 L760,647v-66q-11,6 -22,11t-23,10l-61,-61q30,-8 56.5,-17.5T760,501v-120q-41,23 -94,37t-116,19l-76,-76q44,0 92,-7t89.5,-18.5q41.5,-11.5 70,-26T760,281q-11,-29 -100.5,-55T480,200q-37,0 -75.5,5T331,218l-66,-66q45,-15 100,-23.5t115,-8.5q149,0 254.5,47T840,280v400q0,10 -2.5,19t-7.5,18Z"
android:fillColor="@android:color/black"/>
</vector>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@
<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_calendar_storage_disabled">Calendar provider missing. Did you disable the \"Calendar storage\" system app?</string>
<string name="account_list_contacts_storage_disabled">Contacts provider missing. Did you disable the \"Contacts storage\" system app?</string>
<string name="account_list_manage_apps">Manage apps</string>
<string name="account_list_welcome">Welcome to DAVx⁵!</string>
<string name="account_list_empty">Connect to your server and keep your calendars and contacts synchronized.</string>
<string name="accounts_sync_all">Sync all accounts</string>
Expand Down

0 comments on commit 2d686be

Please sign in to comment.