Skip to content

Commit

Permalink
[android] Fix synchronized contacts not appearing in some contact apps
Browse files Browse the repository at this point in the history
Certain contact apps require the account to be syncable.
In order to do this, we implement a stubbed sync service
and adapter which does nothing and ask Android not to
call it. Preexisting installations of the app will need
to turn contact synchronization off and on again to
receive this fix.
Closes #6568.

Co-authored-by: paw <[email protected]>
  • Loading branch information
rezbyte and paw-hub committed Sep 2, 2024
1 parent def698a commit e2e20fd
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
13 changes: 13 additions & 0 deletions app-android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission
android:name="android.permission.SCHEDULE_EXACT_ALARM"
android:maxSdkVersion="32"/>
Expand Down Expand Up @@ -150,6 +151,18 @@
android:resource="@xml/authenticator"/>
</service>

<service
android:name=".StubSyncService"
android:exported="false"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter"/>
</service>


<receiver
android:name=".push.BootBroadcastReceiver"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class AndroidMobileContactsFacade(private val activity: MainActivity) : MobileCo

override suspend fun deleteContacts(username: String, contactId: String?) {
checkContactPermissions()
checkSyncPermission()

retrieveRawContacts(username, contactId).use { cursor ->
cursor?.forEachRow {
Expand Down Expand Up @@ -138,6 +139,7 @@ class AndroidMobileContactsFacade(private val activity: MainActivity) : MobileCo

override suspend fun saveContacts(username: String, contacts: List<StructuredContact>) {
checkContactPermissions()
checkSyncPermission()
val matchResult = matchStoredContacts(username, contacts, false)
for (contact in matchResult.newServerContacts) {
createContact(username, contact)
Expand Down Expand Up @@ -230,6 +232,7 @@ class AndroidMobileContactsFacade(private val activity: MainActivity) : MobileCo

override suspend fun syncContacts(username: String, contacts: List<StructuredContact>): ContactSyncResult {
checkContactPermissions()
checkSyncPermission()

val matchResult = matchStoredContacts(username, contacts, true)
for (contact in matchResult.newServerContacts) {
Expand All @@ -249,6 +252,10 @@ class AndroidMobileContactsFacade(private val activity: MainActivity) : MobileCo
activity.getPermission(Manifest.permission.WRITE_CONTACTS)
}

private suspend fun checkSyncPermission() {
activity.getPermission(Manifest.permission.WRITE_SYNC_SETTINGS)
}

private fun deleteRawContact(rawId: Long): Int {
val uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawId).buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()
return resolver.delete(uri, null, null)
Expand All @@ -274,6 +281,10 @@ class AndroidMobileContactsFacade(private val activity: MainActivity) : MobileCo

private fun createSystemAccount(username: String) {
val userAccount = Account(username, TUTA_ACCOUNT_TYPE)

// Disable all usage of the stub sync adapter by the OS
ContentResolver.setSyncAutomatically(userAccount, ContactsContract.AUTHORITY, false)

val isAccountAdded = AccountManager.get(activity).addAccountExplicitly(userAccount, null, null)
if (!isAccountAdded) {
Log.w(TAG, "Failed to create new account?")
Expand Down
53 changes: 53 additions & 0 deletions app-android/app/src/main/java/de/tutao/tutanota/StubSyncService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package de.tutao.tutanota

import android.accounts.Account
import android.app.Service
import android.content.AbstractThreadedSyncAdapter
import android.content.ContentProviderClient
import android.content.Context
import android.content.Intent
import android.content.SyncResult
import android.os.Bundle
import android.os.IBinder
import android.util.Log

private const val TAG = "Sync"

/** A stubbed sync service that returns a stubbed sync adapter on `onBind()`.
* It is used to mark Tuta accounts as syncable as a fix for #6568 */
class StubSyncService : Service() {
override fun onCreate() {
synchronized(syncAdapterLock) {
syncAdapter = syncAdapter ?: StubSyncAdapter(applicationContext, true)
}
}

override fun onBind(intent: Intent): IBinder {
return syncAdapter?.syncAdapterBinder ?: throw IllegalStateException()
}

companion object {
private var syncAdapter: StubSyncAdapter? = null
private val syncAdapterLock = Any()
}
}

/** A stubbed sync adapter that does nothing when called. */
private class StubSyncAdapter @JvmOverloads constructor(
context: Context,
autoInitialize: Boolean,
allowParallelSyncs: Boolean = false,
) : AbstractThreadedSyncAdapter(context, autoInitialize, allowParallelSyncs) {

override fun onPerformSync(
account: Account,
extras: Bundle,
authority: String,
provider: ContentProviderClient,
syncResult: SyncResult
) {
Log.w(TAG, "Sync requested to stub Sync Adapter!")
}
}


11 changes: 11 additions & 0 deletions app-android/app/src/main/res/xml/syncadapter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Declare the stubbed sync adapter to fix #6568-->
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="@string/package_name"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
/>

0 comments on commit e2e20fd

Please sign in to comment.