Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24730d19 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Notify on invalid iCalendar/vCard objects

* add notification channel: sync warnings
* notify on invalid iCalendar/vCard objects
* add app setting: Notification settings (only when notification channels are available)
parent 94fd665b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -388,7 +388,7 @@ class DavService: Service() {
                debugIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e)
                debugIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account)

                val notify = NotificationUtils.newBuilder(this)
                val notify = NotificationUtils.newBuilder(this, NotificationUtils.CHANNEL_GENERAL)
                        .setSmallIcon(R.drawable.ic_sync_error_notification)
                        .setContentTitle(getString(R.string.dav_service_refresh_failed))
                        .setContentText(getString(R.string.dav_service_refresh_couldnt_refresh))
+4 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.exception.DavException
import at.bitfire.dav4jvm.property.*
import at.bitfire.davdroid.DavUtils
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.SyncState
import at.bitfire.davdroid.resource.LocalCalendar
@@ -185,4 +186,7 @@ class CalendarSyncManager(
            Logger.log.info("Received VCALENDAR with not exactly one VEVENT with UID and without RECURRENCE-ID; ignoring $fileName")
    }

    override fun notifyInvalidResourceTitle(): String =
            context.getString(R.string.sync_invalid_event)

}
+14 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import at.bitfire.vcard4android.BatchOperation
import at.bitfire.vcard4android.Contact
import at.bitfire.vcard4android.GroupMethod
import ezvcard.VCardVersion
import ezvcard.io.CannotParseException
import okhttp3.HttpUrl
import okhttp3.Request
import okhttp3.RequestBody
@@ -355,18 +356,24 @@ class ContactsSyncManager(
        Logger.log.info("Processing CardDAV resource $fileName")

        // TODO catch and show notification on CannotParseException
        val contacts = Contact.fromReader(reader, downloader)
        val contacts = try {
            Contact.fromReader(reader, downloader)
        } catch (e: CannotParseException) {
            Logger.log.log(Level.SEVERE, "Received invalid vCard, ignoring", e)
            notifyInvalidResource(e, fileName)
            return
        }

        if (contacts.isEmpty()) {
            Logger.log.warning("Received VCard without data, ignoring")
            Logger.log.warning("Received vCard without data, ignoring")
            return
        } else if (contacts.size > 1)
            Logger.log.warning("Received multiple VCards, using first one")
            Logger.log.warning("Received multiple vCards, using first one")

        val newData = contacts.first()

        if (groupMethod == GroupMethod.CATEGORIES && newData.group) {
            Logger.log.warning("Received group VCard although group method is CATEGORIES. Saving as regular contact")
            Logger.log.warning("Received group vCard although group method is CATEGORIES. Saving as regular contact")
            newData.group = false
        }

@@ -476,4 +483,7 @@ class ContactsSyncManager(
        }
    }

    override fun notifyInvalidResourceTitle(): String =
            context.getString(R.string.sync_invalid_contact)

}
+33 −15
Original line number Diff line number Diff line
@@ -663,23 +663,10 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
                    else
                        account)
        } else {
            contentIntent = Intent(context, DebugInfoActivity::class.java)
            contentIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e)

            contentIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account)
            contentIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)

            // use current local/remote resource
            if (local != null) {
                // pass local resource info to debug info
                contentIntent.putExtra(DebugInfoActivity.KEY_LOCAL_RESOURCE, local.toString())

                // generate "view item" action
            contentIntent = buildDebugInfoIntent(e, local, remote)
            if (local != null)
                viewItemAction = buildViewItemAction(local)
        }
            if (remote != null)
                contentIntent.putExtra(DebugInfoActivity.KEY_REMOTE_RESOURCE, remote.toString())
        }

        // to make the PendingIntent unique
        contentIntent.data = Uri.parse("davdroid:exception/${e.hashCode()}")
@@ -710,6 +697,19 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
        notificationManager.notify(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build())
    }

    private fun buildDebugInfoIntent(e: Throwable, local: ResourceType?, remote: HttpUrl?) =
            Intent(context, DebugInfoActivity::class.java).apply {
                putExtra(DebugInfoActivity.KEY_ACCOUNT, account)
                putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
                putExtra(DebugInfoActivity.KEY_THROWABLE, e)

                // pass current local/remote resource
                if (local != null)
                    putExtra(DebugInfoActivity.KEY_LOCAL_RESOURCE, local.toString())
                if (remote != null)
                    putExtra(DebugInfoActivity.KEY_REMOTE_RESOURCE, remote.toString())
            }

    private fun buildRetryAction(): NotificationCompat.Action {
        val retryIntent = Intent(context, DavService::class.java)
        retryIntent.action = DavService.ACTION_FORCE_SYNC
@@ -757,6 +757,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
            null
    }


    fun checkResults(results: MutableCollection<Future<*>>) {
        val iter = results.iterator()
        while (iter.hasNext()) {
@@ -772,6 +773,23 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
        }
    }

    protected fun notifyInvalidResource(e: Throwable, fileName: String) {
        val intent = buildDebugInfoIntent(e, null, collectionURL.resolve(fileName))

        val builder = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_WARNINGS)
        builder .setSmallIcon(R.drawable.ic_warning_notify)
                .setContentTitle(notifyInvalidResourceTitle())
                .setContentText(context.getString(R.string.sync_invalid_resources_ignoring))
                .setSubText(mainAccount.name)
                .setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
                .setAutoCancel(true)
                .setOnlyAlertOnce(true)
                .priority = NotificationCompat.PRIORITY_LOW
        notificationManager.notify(notificationTag, NotificationUtils.NOTIFY_INVALID_RESOURCE, builder.build())
    }

    protected abstract fun notifyInvalidResourceTitle(): String

    protected fun<T: ResourceType?, R> useLocal(local: T, body: (T) -> R): R {
        try {
            return body(local)
+1 −1
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ class TasksSyncAdapterService: SyncAdapterService() {
            } catch (e: TaskProvider.ProviderTooOldException) {
                val nm = NotificationManagerCompat.from(context)
                val message = context.getString(R.string.sync_error_opentasks_required_version, e.provider.minVersionName, e.installedVersionName)
                val notify = NotificationUtils.newBuilder(context)
                val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
                        .setSmallIcon(R.drawable.ic_sync_error_notification)
                        .setContentTitle(context.getString(R.string.sync_error_opentasks_too_old))
                        .setContentText(message)
Loading