mirror of
https://github.com/etesync/android
synced 2024-11-22 07:58:09 +00:00
Don't rely on the ACCOUNTS_CHANGED broadcast receiver
I've seen some crashes there. This change brings it inline with DAVDroid, and looks cleaner regardless. Based on 1f7298f947a4878e86fcba0c6722e34b03cb63c6 from DAVDroid
This commit is contained in:
parent
f629d23c38
commit
3b0bfbb054
@ -164,12 +164,6 @@
|
|||||||
android:enabled="true">
|
android:enabled="true">
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<receiver android:name=".AccountsChangedReceiver">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<receiver android:name=".PackageChangedReceiver">
|
<receiver android:name=".PackageChangedReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PACKAGE_ADDED"/>
|
<action android:name="android.intent.action.PACKAGE_ADDED"/>
|
||||||
|
@ -35,7 +35,6 @@ class AccountUpdateService : Service() {
|
|||||||
val action = intent.action
|
val action = intent.action
|
||||||
|
|
||||||
when (action) {
|
when (action) {
|
||||||
ACTION_ACCOUNTS_UPDATED -> cleanupAccounts()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,45 +76,4 @@ class AccountUpdateService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ACTION RUNNABLES
|
|
||||||
which actually do the work
|
|
||||||
*/
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
internal fun cleanupAccounts() {
|
|
||||||
Logger.log.info("Cleaning up orphaned accounts")
|
|
||||||
|
|
||||||
val accountNames = LinkedList<String>()
|
|
||||||
val am = AccountManager.get(this)
|
|
||||||
for (account in am.getAccountsByType(getString(R.string.account_type))) {
|
|
||||||
accountNames.add(account.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
val data = (application as App).data
|
|
||||||
|
|
||||||
// delete orphaned address book accounts
|
|
||||||
for (addrBookAccount in am.getAccountsByType(getString(R.string.account_type_address_book))) {
|
|
||||||
val addressBook = LocalAddressBook(this, addrBookAccount, null)
|
|
||||||
try {
|
|
||||||
if (!accountNames.contains(addressBook.mainAccount.name))
|
|
||||||
addressBook.delete()
|
|
||||||
} catch (e: ContactsStorageException) {
|
|
||||||
Logger.log.log(Level.SEVERE, "Couldn't get address book main account", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (accountNames.isEmpty()) {
|
|
||||||
data.delete(ServiceEntity::class.java).get().value()
|
|
||||||
} else {
|
|
||||||
data.delete(ServiceEntity::class.java).where(ServiceEntity.ACCOUNT.notIn(accountNames)).get().value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val ACTION_ACCOUNTS_UPDATED = "accountsUpdated"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the GNU Public License v3.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.gnu.org/licenses/gpl.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.etesync.syncadapter
|
|
||||||
|
|
||||||
import android.accounts.AccountManager
|
|
||||||
import android.accounts.OnAccountsUpdateListener
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import com.etesync.syncadapter.log.Logger
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class AccountsChangedReceiver : BroadcastReceiver() {
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION == intent.action) {
|
|
||||||
val serviceIntent = Intent(context, AccountUpdateService::class.java)
|
|
||||||
serviceIntent.action = AccountUpdateService.ACTION_ACCOUNTS_UPDATED
|
|
||||||
try {
|
|
||||||
context.startService(serviceIntent)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
Logger.log.warning("Got an illegal state exception! Ignoring...")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (listener in listeners)
|
|
||||||
listener.onAccountsUpdated(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
protected val listeners: MutableList<OnAccountsUpdateListener> = LinkedList()
|
|
||||||
|
|
||||||
fun registerListener(listener: OnAccountsUpdateListener, callImmediately: Boolean) {
|
|
||||||
listeners.add(listener)
|
|
||||||
if (callImmediately)
|
|
||||||
listener.onAccountsUpdated(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unregisterListener(listener: OnAccountsUpdateListener) {
|
|
||||||
listeners.remove(listener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the GNU Public License v3.0
|
* are made available under the terms of the GNU Public License v3.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
@ -8,65 +8,110 @@
|
|||||||
package com.etesync.syncadapter.syncadapter
|
package com.etesync.syncadapter.syncadapter
|
||||||
|
|
||||||
import android.accounts.*
|
import android.accounts.*
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.database.DatabaseUtils
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import at.bitfire.vcard4android.ContactsStorageException
|
||||||
|
import com.etesync.syncadapter.App
|
||||||
|
import com.etesync.syncadapter.R
|
||||||
|
import com.etesync.syncadapter.log.Logger
|
||||||
|
import com.etesync.syncadapter.model.ServiceDB
|
||||||
|
import com.etesync.syncadapter.model.ServiceEntity
|
||||||
|
import com.etesync.syncadapter.resource.LocalAddressBook
|
||||||
import com.etesync.syncadapter.ui.setup.LoginActivity
|
import com.etesync.syncadapter.ui.setup.LoginActivity
|
||||||
|
import java.util.*
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account authenticator for the main DAVx5 account type.
|
||||||
|
*
|
||||||
|
* Gets started when a DAVx5 account is removed, too, so it also watches for account removals
|
||||||
|
* and contains the corresponding cleanup code.
|
||||||
|
*/
|
||||||
|
class AccountAuthenticatorService: Service(), OnAccountsUpdateListener {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
internal fun cleanupAccounts(context: Context) {
|
||||||
|
Logger.log.info("Cleaning up orphaned accounts")
|
||||||
|
|
||||||
|
val accountNames = LinkedList<String>()
|
||||||
|
val am = AccountManager.get(context)
|
||||||
|
for (account in am.getAccountsByType(context.getString(R.string.account_type))) {
|
||||||
|
accountNames.add(account.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val data = (context.applicationContext as App).data
|
||||||
|
|
||||||
|
// delete orphaned address book accounts
|
||||||
|
for (addrBookAccount in am.getAccountsByType(context.getString(R.string.account_type_address_book))) {
|
||||||
|
val addressBook = LocalAddressBook(context, addrBookAccount, null)
|
||||||
|
try {
|
||||||
|
if (!accountNames.contains(addressBook.mainAccount.name))
|
||||||
|
addressBook.delete()
|
||||||
|
} catch (e: ContactsStorageException) {
|
||||||
|
Logger.log.log(Level.SEVERE, "Couldn't get address book main account", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (accountNames.isEmpty()) {
|
||||||
|
data.delete(ServiceEntity::class.java).get().value()
|
||||||
|
} else {
|
||||||
|
data.delete(ServiceEntity::class.java).where(ServiceEntity.ACCOUNT.notIn(accountNames)).get().value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var accountManager: AccountManager
|
||||||
|
private lateinit var accountAuthenticator: AccountAuthenticator
|
||||||
|
|
||||||
class AccountAuthenticatorService : Service() {
|
|
||||||
|
|
||||||
private var accountAuthenticator: AccountAuthenticator? = null
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
accountManager = AccountManager.get(this)
|
||||||
|
accountManager.addOnAccountsUpdatedListener(this, null, true)
|
||||||
|
|
||||||
accountAuthenticator = AccountAuthenticator(this)
|
accountAuthenticator = AccountAuthenticator(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onDestroy() {
|
||||||
return if (intent.action == android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT) accountAuthenticator!!.iBinder else null
|
super.onDestroy()
|
||||||
|
accountManager.removeOnAccountsUpdatedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?) =
|
||||||
|
accountAuthenticator.iBinder.takeIf { intent?.action == android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT }
|
||||||
|
|
||||||
|
|
||||||
|
override fun onAccountsUpdated(accounts: Array<out Account>?) {
|
||||||
|
cleanupAccounts(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class AccountAuthenticator(internal val context: Context) : AbstractAccountAuthenticator(context) {
|
private class AccountAuthenticator(
|
||||||
|
val context: Context
|
||||||
|
): AbstractAccountAuthenticator(context) {
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
override fun addAccount(response: AccountAuthenticatorResponse?, accountType: String?, authTokenType: String?, requiredFeatures: Array<String>?, options: Bundle?): Bundle {
|
||||||
override fun addAccount(response: AccountAuthenticatorResponse, accountType: String, authTokenType: String,
|
|
||||||
requiredFeatures: Array<String>, options: Bundle): Bundle {
|
|
||||||
val intent = Intent(context, LoginActivity::class.java)
|
val intent = Intent(context, LoginActivity::class.java)
|
||||||
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
||||||
val bundle = Bundle()
|
val bundle = Bundle(1)
|
||||||
bundle.putParcelable(AccountManager.KEY_INTENT, intent)
|
bundle.putParcelable(AccountManager.KEY_INTENT, intent)
|
||||||
return bundle
|
return bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
override fun editProperties(response: AccountAuthenticatorResponse?, accountType: String?) = null
|
||||||
override fun confirmCredentials(response: AccountAuthenticatorResponse, account: Account, options: Bundle): Bundle? {
|
override fun getAuthTokenLabel(p0: String?) = null
|
||||||
return null
|
override fun confirmCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Bundle?) = null
|
||||||
}
|
override fun updateCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||||
|
override fun getAuthToken(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||||
override fun editProperties(response: AccountAuthenticatorResponse, accountType: String): Bundle? {
|
override fun hasFeatures(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Array<out String>?) = null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun getAuthToken(response: AccountAuthenticatorResponse, account: Account, authTokenType: String, options: Bundle): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAuthTokenLabel(authTokenType: String): String? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun hasFeatures(response: AccountAuthenticatorResponse, account: Account, features: Array<String>): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun updateCredentials(response: AccountAuthenticatorResponse, account: Account, authTokenType: String, options: Bundle): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,75 +5,49 @@
|
|||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.gnu.org/licenses/gpl.html
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.etesync.syncadapter.syncadapter
|
package com.etesync.syncadapter.syncadapter
|
||||||
|
|
||||||
import android.accounts.*
|
import android.accounts.AbstractAccountAuthenticator
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.accounts.AccountAuthenticatorResponse
|
||||||
|
import android.accounts.AccountManager
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
|
||||||
import com.etesync.syncadapter.ui.AccountsActivity
|
import com.etesync.syncadapter.ui.AccountsActivity
|
||||||
|
|
||||||
class NullAuthenticatorService : Service() {
|
class NullAuthenticatorService: Service() {
|
||||||
|
|
||||||
private var accountAuthenticator: AccountAuthenticator? = null
|
private lateinit var accountAuthenticator: AccountAuthenticator
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
accountAuthenticator = NullAuthenticatorService.AccountAuthenticator(this)
|
accountAuthenticator = AccountAuthenticator(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent?) =
|
||||||
return if (intent.action == android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT) accountAuthenticator!!.iBinder else null
|
accountAuthenticator.iBinder.takeIf { intent?.action == android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class AccountAuthenticator(internal val context: Context) : AbstractAccountAuthenticator(context) {
|
private class AccountAuthenticator(
|
||||||
|
val context: Context
|
||||||
|
): AbstractAccountAuthenticator(context) {
|
||||||
|
|
||||||
override fun editProperties(response: AccountAuthenticatorResponse, accountType: String): Bundle? {
|
override fun addAccount(response: AccountAuthenticatorResponse?, accountType: String?, authTokenType: String?, requiredFeatures: Array<String>?, options: Bundle?): Bundle {
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun addAccount(response: AccountAuthenticatorResponse, accountType: String, authTokenType: String, requiredFeatures: Array<String>, options: Bundle): Bundle {
|
|
||||||
val intent = Intent(context, AccountsActivity::class.java)
|
val intent = Intent(context, AccountsActivity::class.java)
|
||||||
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
||||||
val bundle = Bundle()
|
val bundle = Bundle(1)
|
||||||
bundle.putParcelable(AccountManager.KEY_INTENT, intent)
|
bundle.putParcelable(AccountManager.KEY_INTENT, intent)
|
||||||
return bundle
|
return bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
override fun editProperties(response: AccountAuthenticatorResponse?, accountType: String?) = null
|
||||||
override fun confirmCredentials(response: AccountAuthenticatorResponse, account: Account, options: Bundle): Bundle? {
|
override fun getAuthTokenLabel(p0: String?) = null
|
||||||
return null
|
override fun confirmCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Bundle?) = null
|
||||||
}
|
override fun updateCredentials(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||||
|
override fun getAuthToken(p0: AccountAuthenticatorResponse?, p1: Account?, p2: String?, p3: Bundle?) = null
|
||||||
|
override fun hasFeatures(p0: AccountAuthenticatorResponse?, p1: Account?, p2: Array<out String>?) = null
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun getAuthToken(response: AccountAuthenticatorResponse, account: Account, authTokenType: String, options: Bundle): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAuthTokenLabel(authTokenType: String): String? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun updateCredentials(response: AccountAuthenticatorResponse, account: Account, authTokenType: String, options: Bundle): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NetworkErrorException::class)
|
|
||||||
override fun hasFeatures(response: AccountAuthenticatorResponse, account: Account, features: Array<String>): Bundle? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAccountRemovalAllowed(response: AccountAuthenticatorResponse, account: Account): Bundle {
|
|
||||||
val result = Bundle()
|
|
||||||
val allowed = false // we don't want users to explicitly delete inner accounts
|
|
||||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, allowed)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -26,7 +26,6 @@ import android.widget.AbsListView
|
|||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.etesync.syncadapter.AccountsChangedReceiver
|
|
||||||
import com.etesync.syncadapter.App
|
import com.etesync.syncadapter.App
|
||||||
import com.etesync.syncadapter.R
|
import com.etesync.syncadapter.R
|
||||||
|
|
||||||
@ -73,19 +72,13 @@ class AccountListFragment : ListFragment(), LoaderManager.LoaderCallbacks<Array<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class AccountLoader(context: Context) : AsyncTaskLoader<Array<Account>>(context), OnAccountsUpdateListener {
|
private class AccountLoader(context: Context) : AsyncTaskLoader<Array<Account>>(context), OnAccountsUpdateListener {
|
||||||
private val accountManager: AccountManager
|
private val accountManager = AccountManager.get(context)
|
||||||
|
|
||||||
init {
|
override fun onStartLoading() =
|
||||||
accountManager = AccountManager.get(context)
|
accountManager.addOnAccountsUpdatedListener(this, null, true)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartLoading() {
|
override fun onStopLoading() =
|
||||||
AccountsChangedReceiver.registerListener(this, true)
|
accountManager.removeOnAccountsUpdatedListener(this)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStopLoading() {
|
|
||||||
AccountsChangedReceiver.unregisterListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAccountsUpdated(accounts: Array<Account>) {
|
override fun onAccountsUpdated(accounts: Array<Account>) {
|
||||||
forceLoad()
|
forceLoad()
|
||||||
|
Loading…
Reference in New Issue
Block a user