Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show a warning if calendar or contacts storage is deactivated or missing #1243

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"
sunkup marked this conversation as resolved.
Show resolved Hide resolved
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
Loading