diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5ba50cf8b..c33eff686 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,7 +30,7 @@ android { buildConfigField("String", "userAgent", "\"DAVx5\"") - testInstrumentationRunner = "at.bitfire.davdroid.CustomTestRunner" + testInstrumentationRunner = "at.bitfire.davdroid.HiltTestRunner" } java { diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/CustomTestRunner.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/HiltTestRunner.kt similarity index 79% rename from app/src/androidTest/kotlin/at/bitfire/davdroid/CustomTestRunner.kt rename to app/src/androidTest/kotlin/at/bitfire/davdroid/HiltTestRunner.kt index 53d072300..76e166c98 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/CustomTestRunner.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/HiltTestRunner.kt @@ -4,14 +4,14 @@ package at.bitfire.davdroid +import android.app.Application import android.content.Context import androidx.test.runner.AndroidJUnitRunner -import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.testing.HiltTestApplication -class CustomTestRunner : AndroidJUnitRunner() { +class HiltTestRunner : AndroidJUnitRunner() { - override fun newApplication(cl: ClassLoader, name: String, context: Context) = + override fun newApplication(cl: ClassLoader, name: String, context: Context): Application = super.newApplication(cl, HiltTestApplication::class.java.name, context) } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/InitCalendarProviderRule.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/InitCalendarProviderRule.kt index 56dce0710..29f40c0bc 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/InitCalendarProviderRule.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/InitCalendarProviderRule.kt @@ -10,7 +10,6 @@ import android.content.ContentUris import android.content.ContentValues import android.os.Build import android.provider.CalendarContract -import androidx.annotation.RequiresApi import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import at.bitfire.davdroid.log.Logger diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt index e8fc487c5..9ebf6f118 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/TestModules.kt @@ -1,11 +1,11 @@ package at.bitfire.davdroid -import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.push.PushRegistrationWorker +import at.bitfire.davdroid.repository.DavCollectionRepository import dagger.Module import dagger.hilt.components.SingletonComponent -import dagger.multibindings.Multibinds import dagger.hilt.testing.TestInstallIn +import dagger.multibindings.Multibinds interface TestModules { @@ -14,9 +14,10 @@ interface TestModules { components = [SingletonComponent::class], replaces = [PushRegistrationWorker.PushRegistrationWorkerModule::class] ) - abstract class FakePushRegistrationWorkerModule { - // Provides empty set of listeners + abstract class TestPushRegistrationWorkerModule { + // provides empty set of listeners @Multibinds abstract fun defaultOnChangeListeners(): Set } + } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/db/CollectionTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/db/CollectionTest.kt index 4394ddb3e..ade1d499a 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/db/CollectionTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/db/CollectionTest.kt @@ -4,13 +4,14 @@ package at.bitfire.davdroid.db +import android.content.Context import android.security.NetworkSecurityPolicy import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.dav4jvm.DavResource import at.bitfire.dav4jvm.property.webdav.ResourceType import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.HttpUrl.Companion.toHttpUrl @@ -34,25 +35,25 @@ class CollectionTest { val hiltRule = HiltAndroidRule(this) @Inject - lateinit var settingsManager: SettingsManager - - @Before - fun inject() { - hiltRule.inject() - } + @ApplicationContext + lateinit var context: Context + @Inject + lateinit var settingsManager: SettingsManager private lateinit var httpClient: HttpClient private val server = MockWebServer() @Before - fun setUp() { - httpClient = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext).build() + fun setup() { + hiltRule.inject() + + httpClient = HttpClient.Builder(context).build() Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted) } @After - fun shutDown() { + fun teardown() { httpClient.close() } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/network/ConnectionUtilsTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/network/ConnectionUtilsTest.kt index 141bbc9cf..d83ced54e 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/network/ConnectionUtilsTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/network/ConnectionUtilsTest.kt @@ -11,7 +11,6 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.TRANSPORT_WIFI -import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.every import io.mockk.junit4.MockKRule import io.mockk.mockk @@ -21,7 +20,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -@HiltAndroidTest class ConnectionUtilsTest { @get:Rule diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/network/HttpClientTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/network/HttpClientTest.kt index 8245a1f39..2dc7ecf7b 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/network/HttpClientTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/network/HttpClientTest.kt @@ -4,8 +4,9 @@ package at.bitfire.davdroid.network +import android.content.Context import android.security.NetworkSecurityPolicy -import androidx.test.platform.app.InstrumentationRegistry +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.Request @@ -19,6 +20,7 @@ import org.junit.Assume import org.junit.Before import org.junit.Rule import org.junit.Test +import javax.inject.Inject @HiltAndroidTest class HttpClientTest { @@ -29,11 +31,15 @@ class HttpClientTest { @get:Rule var hiltRule = HiltAndroidRule(this) + @Inject + @ApplicationContext + lateinit var context: Context + @Before fun setUp() { hiltRule.inject() - httpClient = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext).build() + httpClient = HttpClient.Builder(context).build() server = MockWebServer() server.start(30000) diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/repository/DavCollectionRepositoryTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/repository/DavCollectionRepositoryTest.kt index 0eb90f191..5f572aeb7 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/repository/DavCollectionRepositoryTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/repository/DavCollectionRepositoryTest.kt @@ -1,9 +1,11 @@ package at.bitfire.davdroid.repository -import androidx.test.platform.app.InstrumentationRegistry +import android.content.Context import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Service +import at.bitfire.davdroid.settings.AccountSettings +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.mockk @@ -23,12 +25,17 @@ class DavCollectionRepositoryTest { var hiltRule = HiltAndroidRule(this) @Inject - lateinit var serviceRepository: DavServiceRepository + lateinit var accountSettingsFactory: AccountSettings.Factory + + @Inject + @ApplicationContext + lateinit var context: Context @Inject lateinit var db: AppDatabase - val context = InstrumentationRegistry.getInstrumentation().targetContext + @Inject + lateinit var serviceRepository: DavServiceRepository var service: Service? = null @@ -44,6 +51,7 @@ class DavCollectionRepositoryTest { serviceRepository.deleteAll() } + @Test fun testOnChangeListener_setForceReadOnly() = runBlocking { val collectionId = db.collectionDao().insertOrUpdateByUrl( @@ -55,7 +63,7 @@ class DavCollectionRepositoryTest { ) ) val testObserver = mockk(relaxed = true) - val collectionRepository = DavCollectionRepository(context, mutableSetOf(testObserver), serviceRepository, db) + val collectionRepository = DavCollectionRepository(accountSettingsFactory, context, db, mutableSetOf(testObserver), serviceRepository) assert(db.collectionDao().get(collectionId)?.forceReadOnly == false) verify(exactly = 0) { @@ -76,4 +84,5 @@ class DavCollectionRepositoryTest { val serviceId = serviceRepository.insertOrReplace(service) return serviceRepository.get(serviceId) } + } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt index 29cb8a66c..4e84f4e10 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookTest.kt @@ -6,15 +6,16 @@ package at.bitfire.davdroid.resource import android.accounts.Account import android.accounts.AccountManager -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.test.platform.app.InstrumentationRegistry +import android.content.Context import at.bitfire.davdroid.R +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import javax.inject.Inject @HiltAndroidTest class LocalAddressBookTest { @@ -22,18 +23,20 @@ class LocalAddressBookTest { @get:Rule val hiltRule = HiltAndroidRule(this) - val context = InstrumentationRegistry.getInstrumentation().targetContext + @Inject + @ApplicationContext + lateinit var context: Context - val mainAccountType = context.getString(R.string.account_type) - val mainAccount = Account("main", mainAccountType) + private val mainAccountType by lazy { context.getString(R.string.account_type) } + private val mainAccount by lazy { Account("main", mainAccountType) } - val addressBookAccountType = context.getString(R.string.account_type_address_book) - val addressBookAccount = Account("sub", addressBookAccountType) + private val addressBookAccountType by lazy { context.getString(R.string.account_type_address_book) } + private val addressBookAccount by lazy { Account("sub", addressBookAccountType) } - val accountManager = AccountManager.get(context) + private val accountManager by lazy { AccountManager.get(context) } @Before - fun setUp() { + fun setup() { hiltRule.inject() // TODO DOES NOT WORK: the account immediately starts to sync, which creates the sync adapter services. @@ -42,7 +45,7 @@ class LocalAddressBookTest { } @After - fun cleanup() { + fun teardown() { accountManager.removeAccount(addressBookAccount, null, null) accountManager.removeAccount(mainAccount, null, null) } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt index 2b8c37816..df9b2ee1c 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalCalendarTest.kt @@ -15,14 +15,19 @@ import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.InitCalendarProviderRule import at.bitfire.ical4android.AndroidCalendar import at.bitfire.ical4android.Event -import at.bitfire.ical4android.util.MiscUtils.closeCompat import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter +import at.bitfire.ical4android.util.MiscUtils.closeCompat import net.fortuna.ical4j.model.property.DtStart import net.fortuna.ical4j.model.property.RRule import net.fortuna.ical4j.model.property.RecurrenceId import net.fortuna.ical4j.model.property.Status -import org.junit.* +import org.junit.After +import org.junit.AfterClass import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Test import org.junit.rules.TestRule class LocalCalendarTest { diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt index 8c59168b2..c07843cda 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalEventTest.kt @@ -21,11 +21,22 @@ import at.techbee.jtx.JtxContract.asSyncAdapter import net.fortuna.ical4j.model.Date import net.fortuna.ical4j.model.DateList import net.fortuna.ical4j.model.parameter.Value -import net.fortuna.ical4j.model.property.* -import org.junit.* -import org.junit.Assert.* +import net.fortuna.ical4j.model.property.DtStart +import net.fortuna.ical4j.model.property.ExDate +import net.fortuna.ical4j.model.property.RRule +import net.fortuna.ical4j.model.property.RecurrenceId +import net.fortuna.ical4j.model.property.Status +import org.junit.After +import org.junit.AfterClass +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Test import org.junit.rules.TestRule -import java.util.* +import java.util.UUID class LocalEventTest { diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt index 3d66578fa..d04eb2c4b 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalGroupTest.kt @@ -8,6 +8,7 @@ import android.Manifest import android.content.ContentProviderClient import android.content.ContentUris import android.content.ContentValues +import android.content.Context import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds.GroupMembership import androidx.test.platform.app.InstrumentationRegistry @@ -17,68 +18,70 @@ import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.CachedGroupMembership import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.GroupMethod +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import org.junit.* -import org.junit.Assert.* +import org.junit.AfterClass +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test import javax.inject.Inject @HiltAndroidTest class LocalGroupTest { - @get:Rule - val hiltRule = HiltAndroidRule(this) - - @Inject - lateinit var settingsManager: SettingsManager - - @Before - fun inject() { - hiltRule.inject() - } - companion object { + @JvmField @ClassRule val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! private lateinit var provider: ContentProviderClient - private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook - private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook - @BeforeClass @JvmStatic fun connect() { val context = InstrumentationRegistry.getInstrumentation().targetContext provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! assertNotNull(provider) - - addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) - addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) } @AfterClass @JvmStatic fun disconnect() { - @Suppress("DEPRECATION") - provider.release() + provider.close() } } - private fun newGroup(addressBook: LocalAddressBook = addressBookGroupsAsCategories): LocalGroup = - LocalGroup(addressBook, - Contact().apply { - displayName = "Test Group" - }, null, null, 0 - ).apply { - add() - } + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Inject + @ApplicationContext + lateinit var context: Context + @Inject + lateinit var settingsManager: SettingsManager + private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook + private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook + @Before - fun clearContacts() { + fun setup() { + hiltRule.inject() + + addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) + addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) + + // clear contacts addressBookGroupsAsCategories.clear() addressBookGroupsAsVCards.clear() } @@ -264,4 +267,16 @@ class LocalGroupTest { assertEquals("$newUid.vcf", fileName) } + + // helpers + + private fun newGroup(addressBook: LocalAddressBook = addressBookGroupsAsCategories): LocalGroup = + LocalGroup(addressBook, + Contact().apply { + displayName = "Test Group" + }, null, null, 0 + ).apply { + add() + } + } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt index 1f1b08420..09612eeaf 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/CachedGroupMembershipHandlerTest.kt @@ -7,6 +7,7 @@ package at.bitfire.davdroid.resource.contactrow import android.Manifest import android.content.ContentProviderClient import android.content.ContentValues +import android.content.Context import android.provider.ContactsContract import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule @@ -15,9 +16,19 @@ import at.bitfire.davdroid.resource.LocalTestAddressBook import at.bitfire.vcard4android.CachedGroupMembership import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.GroupMethod -import org.junit.* +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.AfterClass import org.junit.Assert.assertArrayEquals +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import javax.inject.Inject +@HiltAndroidTest class CachedGroupMembershipHandlerTest { companion object { @@ -27,30 +38,40 @@ class CachedGroupMembershipHandlerTest { val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! private lateinit var provider: ContentProviderClient - private lateinit var addressBook: LocalTestAddressBook @BeforeClass @JvmStatic fun connect() { val context = InstrumentationRegistry.getInstrumentation().context provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - Assert.assertNotNull(provider) - - addressBook = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) } @AfterClass @JvmStatic fun disconnect() { - @Suppress("DEPRECATION") - provider.release() + provider.close() } } + @Inject + @ApplicationContext + lateinit var context: Context + + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Before + fun inject() { + hiltRule.inject() + } + + @Test fun testMembership() { + val addressBook = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) + val contact = Contact() val localContact = LocalContact(addressBook, contact, null, null, 0) CachedGroupMembershipHandler(localContact).handle(ContentValues().apply { diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt index 9a09916c9..311c5529d 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipBuilderTest.kt @@ -6,6 +6,7 @@ package at.bitfire.davdroid.resource.contactrow import android.Manifest import android.content.ContentProviderClient +import android.content.Context import android.net.Uri import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds.GroupMembership @@ -14,38 +15,54 @@ import androidx.test.rule.GrantPermissionRule import at.bitfire.davdroid.resource.LocalTestAddressBook import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.GroupMethod -import org.junit.* +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.AfterClass import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import javax.inject.Inject +@HiltAndroidTest class GroupMembershipBuilderTest { companion object { + @JvmField @ClassRule val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! private lateinit var provider: ContentProviderClient - private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook - private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook - @BeforeClass @JvmStatic fun connect() { - val context = InstrumentationRegistry.getInstrumentation().targetContext + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - Assert.assertNotNull(provider) - - addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) - addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) } @AfterClass @JvmStatic fun disconnect() { - @Suppress("DEPRECATION") - provider.release() + provider.close() } + + } + + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Inject + @ApplicationContext + lateinit var context: Context + + @Before + fun inject() { + hiltRule.inject() } @@ -54,6 +71,7 @@ class GroupMembershipBuilderTest { val contact = Contact().apply { categories += "TEST GROUP" } + val addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsCategories, false).build().also { result -> assertEquals(1, result.size) assertEquals(GroupMembership.CONTENT_ITEM_TYPE, result[0].values[GroupMembership.MIMETYPE]) @@ -66,6 +84,7 @@ class GroupMembershipBuilderTest { val contact = Contact().apply { categories += "TEST GROUP" } + val addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) GroupMembershipBuilder(Uri.EMPTY, null, contact, addressBookGroupsAsVCards, false).build().also { result -> // group membership is constructed during post-processing assertEquals(0, result.size) diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt index ffbfe2c22..ef7a81faa 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/contactrow/GroupMembershipHandlerTest.kt @@ -7,6 +7,7 @@ package at.bitfire.davdroid.resource.contactrow import android.Manifest import android.content.ContentProviderClient import android.content.ContentValues +import android.content.Context import android.provider.ContactsContract import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule @@ -15,44 +16,64 @@ import at.bitfire.davdroid.resource.LocalTestAddressBook import at.bitfire.vcard4android.CachedGroupMembership import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.GroupMethod -import org.junit.* +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.AfterClass +import org.junit.Assert import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertTrue - +import org.junit.Before +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import javax.inject.Inject + +@HiltAndroidTest class GroupMembershipHandlerTest { - @JvmField - @Rule - val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! - - private lateinit var provider: ContentProviderClient + companion object { - private lateinit var addressBookGroupsAsCategories: LocalTestAddressBook - private var addressBookGroupsAsCategoriesGroup: Long = -1 + @JvmField + @ClassRule + val permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)!! - private lateinit var addressBookGroupsAsVCards: LocalTestAddressBook + private lateinit var provider: ContentProviderClient - @Before - fun connect() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! - Assert.assertNotNull(provider) + @BeforeClass + @JvmStatic + fun connect() { + val context: Context = InstrumentationRegistry.getInstrumentation().context + provider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)!! + Assert.assertNotNull(provider) + } - addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) - addressBookGroupsAsCategoriesGroup = addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP") + @AfterClass + @JvmStatic + fun disconnect() { + provider.close() + } - addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) } - @After - fun disconnect() { - @Suppress("DEPRECATION") - provider.release() + @Inject @ApplicationContext + lateinit var context: Context + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Before + fun inject() { + hiltRule.inject() } @Test fun testMembership_GroupsAsCategories() { + val addressBookGroupsAsCategories = LocalTestAddressBook(context, provider, GroupMethod.CATEGORIES) + val addressBookGroupsAsCategoriesGroup = addressBookGroupsAsCategories.findOrCreateGroup("TEST GROUP") + val contact = Contact() val localContact = LocalContact(addressBookGroupsAsCategories, contact, null, null, 0) GroupMembershipHandler(localContact).handle(ContentValues().apply { @@ -66,6 +87,8 @@ class GroupMembershipHandlerTest { @Test fun testMembership_GroupsAsVCards() { + val addressBookGroupsAsVCards = LocalTestAddressBook(context, provider, GroupMethod.GROUP_VCARDS) + val contact = Contact() val localContact = LocalContact(addressBookGroupsAsVCards, contact, null, null, 0) GroupMembershipHandler(localContact).handle(ContentValues().apply { diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/CollectionListRefresherTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/CollectionListRefresherTest.kt index e144109d2..5a0f7d7df 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/CollectionListRefresherTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/CollectionListRefresherTest.kt @@ -6,7 +6,6 @@ package at.bitfire.davdroid.servicedetection import android.content.Context import android.security.NetworkSecurityPolicy -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.HomeSet @@ -16,6 +15,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.settings.Settings import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.every @@ -36,22 +36,6 @@ import javax.inject.Inject @HiltAndroidTest class CollectionListRefresherTest { - - @get:Rule - var hiltRule = HiltAndroidRule(this) - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - - @Inject - lateinit var settings: SettingsManager - - @Before - fun setUp() { - hiltRule.inject() - } - - - // Test dependencies companion object { private const val PATH_CALDAV = "/caldav" @@ -66,28 +50,40 @@ class CollectionListRefresherTest { private const val SUBPATH_ADDRESSBOOK_INACCESSIBLE = "/addressbooks/inaccessible-contacts" } + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + @ApplicationContext + lateinit var context: Context + @Inject lateinit var db: AppDatabase @Inject lateinit var refresherFactory: CollectionListRefresher.Factory + @Inject + lateinit var settings: SettingsManager + private val mockServer = MockWebServer() private lateinit var client: HttpClient @Before - fun mockServerSetup() { + fun setup() { + hiltRule.inject() + // Start mock web server mockServer.dispatcher = TestDispatcher() mockServer.start() - client = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext).build() + client = HttpClient.Builder(context).build() Assume.assumeTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted) } @After - fun cleanUp() { + fun teardown() { mockServer.shutdown() db.close() } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/DavResourceFinderTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/DavResourceFinderTest.kt index ea0ca7dc4..4ceeb8c09 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/DavResourceFinderTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/servicedetection/DavResourceFinderTest.kt @@ -4,9 +4,9 @@ package at.bitfire.davdroid.servicedetection +import android.content.Context import android.security.NetworkSecurityPolicy import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.dav4jvm.DavResource import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet import at.bitfire.dav4jvm.property.webdav.ResourceType @@ -15,6 +15,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.servicedetection.DavResourceFinder.Configuration.ServiceInfo import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.mockwebserver.Dispatcher @@ -37,17 +38,6 @@ import javax.inject.Inject @HiltAndroidTest class DavResourceFinderTest { - @get:Rule - val hiltRule = HiltAndroidRule(this) - - @Inject - lateinit var settingsManager: SettingsManager - - @Before - fun inject() { - hiltRule.inject() - } - companion object { private const val PATH_NO_DAV = "/nodav" private const val PATH_CALDAV = "/caldav" @@ -59,21 +49,33 @@ class DavResourceFinderTest { private const val SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts" } + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Inject + @ApplicationContext + lateinit var context: Context + + @Inject + lateinit var settingsManager: SettingsManager + private val server = MockWebServer() private lateinit var finder: DavResourceFinder private lateinit var client: HttpClient @Before - fun initServerAndClient() { + fun setup() { + hiltRule.inject() + server.dispatcher = TestDispatcher() server.start() val baseURI = URI.create("/") val credentials = Credentials("mock", "12345") - finder = DavResourceFinder(InstrumentationRegistry.getInstrumentation().targetContext, baseURI, credentials) - client = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext) + finder = DavResourceFinder(context, baseURI, credentials) + client = HttpClient.Builder(context) .addAuthentication(null, credentials) .build() @@ -81,7 +83,7 @@ class DavResourceFinderTest { } @After - fun stopServer() { + fun teardown() { server.shutdown() } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProviderTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProviderTest.kt index e46dbedfb..f7b6796d4 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProviderTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProviderTest.kt @@ -6,13 +6,13 @@ package at.bitfire.davdroid.webdav import android.content.Context import android.security.NetworkSecurityPolicy -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.WebDavDocument import at.bitfire.davdroid.db.WebDavMount import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.network.HttpClient +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.CookieJar @@ -31,10 +31,16 @@ import javax.inject.Inject @HiltAndroidTest class DavDocumentsProviderTest { + companion object { + private const val PATH_WEBDAV_ROOT = "/webdav" + } + @get:Rule val hiltRule = HiltAndroidRule(this) - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + @Inject + @ApplicationContext + lateinit var context: Context @Inject lateinit var db: AppDatabase @@ -43,13 +49,11 @@ class DavDocumentsProviderTest { hiltRule.inject() } + private var mockServer = MockWebServer() private lateinit var client: HttpClient - companion object { - private const val PATH_WEBDAV_ROOT = "/webdav" - } @Before fun mockServerSetup() { @@ -57,7 +61,7 @@ class DavDocumentsProviderTest { mockServer.dispatcher = TestDispatcher() mockServer.start() - client = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext).build() + client = HttpClient.Builder(context).build() // mock server delivers HTTP without encryption assertTrue(NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted) diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/OkhttpClientTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/OkhttpClientTest.kt index adfae42e2..687c48b07 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/OkhttpClientTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/OkhttpClientTest.kt @@ -4,9 +4,10 @@ package at.bitfire.davdroid -import androidx.test.platform.app.InstrumentationRegistry +import android.content.Context import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.Request @@ -21,6 +22,10 @@ class OkhttpClientTest { @get:Rule val hiltRule = HiltAndroidRule(this) + @Inject + @ApplicationContext + lateinit var context: Context + @Inject lateinit var settingsManager: SettingsManager @@ -32,8 +37,7 @@ class OkhttpClientTest { @Test fun testIcloudWithSettings() { - val client = HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext) - .build() + val client = HttpClient.Builder(context).build() client.okHttpClient.newCall(Request.Builder() .get() .url("https://icloud.com") diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt index 1fe9d9a7f..15bdf2586 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncManagerTest.kt @@ -6,6 +6,7 @@ package at.bitfire.davdroid.sync import android.accounts.Account import android.accounts.AccountManager +import android.content.Context import android.content.SyncResult import android.util.Log import androidx.core.app.NotificationManagerCompat @@ -26,6 +27,7 @@ import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.NotificationUtils +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.mockk @@ -75,6 +77,13 @@ class SyncManagerTest { @get:Rule val hiltRule = HiltAndroidRule(this) + @Inject + lateinit var accountSettingsFactory: AccountSettings.Factory + + @Inject + @ApplicationContext + lateinit var context: Context + @Inject lateinit var db: AppDatabase @@ -107,9 +116,10 @@ class SyncManagerTest { ) = TestSyncManager( account, + accountSettingsFactory.forAccount(account), arrayOf(), "TestAuthority", - HttpClient.Builder(InstrumentationRegistry.getInstrumentation().targetContext).build(), + HttpClient.Builder(context).build(), syncResult, localCollection, collection, diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt index 8bbe573ae..dc1b93f96 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt @@ -8,10 +8,11 @@ import android.accounts.Account import android.content.ContentProviderClient import android.content.Context import android.content.SyncResult -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.network.HttpClient +import at.bitfire.davdroid.settings.AccountSettings +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Assert.assertEquals @@ -27,25 +28,30 @@ class SyncerTest { @get:Rule val hiltRule = HiltAndroidRule(this) - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + @Inject + @ApplicationContext + lateinit var context: Context + + @Inject + lateinit var accountSettingsFactory: AccountSettings.Factory @Inject lateinit var db: AppDatabase /** use our WebDAV provider as a mock provider because it's our own and we don't need any permissions for it */ - private val mockAuthority = context.getString(R.string.webdav_authority) - - val account = Account(javaClass.canonicalName, context.getString(R.string.account_type)) + private val mockAuthority by lazy { context.getString(R.string.webdav_authority) } + val account by lazy { Account(javaClass.canonicalName, context.getString(R.string.account_type)) } @Before fun setUp() { hiltRule.inject() } + @Test fun testOnPerformSync_runsSyncAndSetsClassLoader() { - val syncer = TestSyncer(context, db) + val syncer = TestSyncer(accountSettingsFactory, context, db) syncer.onPerformSync(account, arrayOf(), mockAuthority, SyncResult()) // check whether onPerformSync() actually calls sync() @@ -56,7 +62,11 @@ class SyncerTest { } - class TestSyncer(context: Context, db: AppDatabase) : Syncer(context, db) { + class TestSyncer( + accountSettingsFactory: AccountSettings.Factory, + context: Context, + db: AppDatabase + ) : Syncer(accountSettingsFactory, context, db) { val syncCalled = AtomicInteger() diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/TestSyncManager.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/TestSyncManager.kt index 6b2ffaeaa..dfef691ad 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/TestSyncManager.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/TestSyncManager.kt @@ -26,6 +26,7 @@ import org.junit.Assert.assertEquals class TestSyncManager( account: Account, + accountSettings: AccountSettings, extras: Array, authority: String, httpClient: HttpClient, @@ -35,7 +36,7 @@ class TestSyncManager( val mockWebServer: MockWebServer, context: Context, db: AppDatabase -): SyncManager(account, AccountSettings(context, account), httpClient, extras, authority, syncResult, localCollection, collection, context, db) { +): SyncManager(account, accountSettings, httpClient, extras, authority, syncResult, localCollection, collection, context, db) { override fun prepare(): Boolean { collectionURL = mockWebServer.url("/") diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/account/AccountUtilsTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/account/AccountUtilsTest.kt index a9f27262d..af684e3bf 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/account/AccountUtilsTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/account/AccountUtilsTest.kt @@ -6,10 +6,11 @@ package at.bitfire.davdroid.sync.account import android.accounts.Account import android.accounts.AccountManager +import android.content.Context import android.os.Bundle -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.R import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Assert.assertEquals @@ -26,6 +27,10 @@ class AccountUtilsTest { @get:Rule val hiltRule = HiltAndroidRule(this) + @Inject + @ApplicationContext + lateinit var context: Context + @Inject lateinit var settingsManager: SettingsManager @@ -35,9 +40,9 @@ class AccountUtilsTest { } - val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext } val account by lazy { Account("Test Account", context.getString(R.string.account_type)) } + @Test fun testCreateAccount() { val userData = Bundle(2) diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorkerTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorkerTest.kt index 7527adfad..b06bdb5dd 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorkerTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorkerTest.kt @@ -7,13 +7,13 @@ package at.bitfire.davdroid.sync.worker import android.accounts.Account import android.accounts.AccountManager import android.content.ContentResolver +import android.content.Context import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import android.provider.CalendarContract import android.provider.ContactsContract import android.util.Log import androidx.core.content.getSystemService -import androidx.test.platform.app.InstrumentationRegistry import androidx.work.Configuration import androidx.work.testing.WorkManagerTestInitHelper import at.bitfire.davdroid.R @@ -23,6 +23,7 @@ import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.sync.account.AccountUtils import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.util.PermissionUtils +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.every @@ -36,27 +37,31 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test +import javax.inject.Inject @HiltAndroidTest class BaseSyncWorkerTest { - val context = InstrumentationRegistry.getInstrumentation().targetContext - - private val accountManager = AccountManager.get(context) - private val account = Account("Test Account", context.getString(R.string.account_type)) - private val fakeCredentials = Credentials("test", "test") - @get:Rule val hiltRule = HiltAndroidRule(this) @get:Rule val mockkRule = MockKRule(this) - @Before - fun inject() = hiltRule.inject() + @Inject + lateinit var accountSettingsFactory: AccountSettings.Factory + @Inject + @ApplicationContext + lateinit var context: Context + + private val accountManager by lazy { AccountManager.get(context) } + private val account by lazy { Account("Test Account", context.getString(R.string.account_type)) } + private val fakeCredentials = Credentials("test", "test") @Before - fun setUp() { + fun setup() { + hiltRule.inject() + assertTrue(AccountUtils.createAccount(context, account, AccountSettings.initialUserData(fakeCredentials))) ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1) ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0) @@ -73,7 +78,7 @@ class BaseSyncWorkerTest { } @After - fun removeAccount() { + fun teardown() { accountManager.removeAccountExplicitly(account) } @@ -87,7 +92,7 @@ class BaseSyncWorkerTest { @Test fun testWifiConditionsMet_anyWifi_wifiEnabled() { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) accountSettings.setSyncWiFiOnly(true) mockkObject(ConnectionUtils) @@ -100,7 +105,7 @@ class BaseSyncWorkerTest { @Test fun testWifiConditionsMet_anyWifi_wifiDisabled() { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) accountSettings.setSyncWiFiOnly(true) mockkObject(ConnectionUtils) @@ -114,7 +119,7 @@ class BaseSyncWorkerTest { @Test fun testCorrectWifiSsid_CorrectWiFiSsid() { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) mockkObject(accountSettings) every { accountSettings.getSyncWifiOnlySSIDs() } returns listOf("SampleWiFi1","ConnectedWiFi") @@ -132,7 +137,7 @@ class BaseSyncWorkerTest { @Test fun testCorrectWifiSsid_WrongWiFiSsid() { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) mockkObject(accountSettings) every { accountSettings.getSyncWifiOnlySSIDs() } returns listOf("SampleWiFi1","SampleWiFi2") diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorkerTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorkerTest.kt index 9862c28f1..dae7d61b0 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorkerTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorkerTest.kt @@ -6,17 +6,31 @@ package at.bitfire.davdroid.sync.worker import android.content.Context import android.provider.CalendarContract -import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.TestUtils import at.bitfire.davdroid.sync.SyncManagerTest.Companion.account +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Assert +import org.junit.Before +import org.junit.Rule import org.junit.Test +import javax.inject.Inject @HiltAndroidTest class OneTimeSyncWorkerTest { - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + @Inject + @ApplicationContext + lateinit var context: Context + + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Before + fun setup() { + hiltRule.inject() + } @Test fun testEnqueue_enqueuesWorker() { diff --git a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt index a00b73ac7..3cab133b5 100644 --- a/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt +++ b/app/src/androidTestOse/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorkerTest.kt @@ -27,6 +27,7 @@ import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.sync.account.AccountUtils import at.bitfire.davdroid.ui.NotificationUtils import dagger.assisted.AssistedFactory +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.junit4.MockKRule @@ -47,7 +48,7 @@ class PeriodicSyncWorkerTest { companion object { - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext private val accountManager = AccountManager.get(context) private val account = Account("Test Account", context.getString(R.string.account_type)) @@ -84,14 +85,18 @@ class PeriodicSyncWorkerTest { fun create(appContext: Context, workerParams: WorkerParameters): PeriodicSyncWorker } + @Inject + @ApplicationContext + lateinit var context: Context + + @Inject + lateinit var syncWorkerFactory: PeriodicSyncWorkerFactory + @get:Rule val hiltRule = HiltAndroidRule(this) @get:Rule val mockkRule = MockKRule(this) - @Inject - lateinit var syncWorkerFactory: PeriodicSyncWorkerFactory - @Before fun inject() { hiltRule.inject() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt index 390c96637..99d88e97d 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt @@ -55,6 +55,7 @@ import javax.inject.Inject class PushRegistrationWorker @AssistedInject constructor( @Assisted context: Context, @Assisted workerParameters: WorkerParameters, + private val accountSettingsFactory: AccountSettings.Factory, private val collectionRepository: DavCollectionRepository, private val preferenceRepository: PreferenceRepository, private val serviceRepository: DavServiceRepository @@ -84,7 +85,7 @@ class PushRegistrationWorker @AssistedInject constructor( private suspend fun requestPushRegistration(collection: Collection, account: Account, endpoint: String) { - val settings = AccountSettings(applicationContext, account) + val settings = accountSettingsFactory.forAccount(account) runInterruptible { HttpClient.Builder(applicationContext, settings) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt index 45f2b9bac..273d64bf2 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt @@ -8,8 +8,8 @@ import android.Manifest import android.accounts.Account import android.accounts.AccountManager import android.accounts.OnAccountsUpdateListener -import android.app.Application import android.content.ContentResolver +import android.content.Context import android.content.pm.PackageManager import android.provider.CalendarContract import android.provider.ContactsContract @@ -32,6 +32,7 @@ import at.bitfire.davdroid.sync.worker.BaseSyncWorker import at.bitfire.davdroid.sync.worker.PeriodicSyncWorker import at.bitfire.davdroid.util.TaskUtils import at.bitfire.vcard4android.GroupMethod +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow @@ -47,7 +48,8 @@ import javax.inject.Inject * [at.bitfire.davdroid.resource.LocalAddressBook]. */ class AccountRepository @Inject constructor( - val context: Application, + private val accountSettingsFactory: AccountSettings.Factory, + @ApplicationContext val context: Context, private val homeSetRepository: DavHomeSetRepository, private val settingsManager: SettingsManager, private val serviceRepository: DavServiceRepository, @@ -83,7 +85,7 @@ class AccountRepository @Inject constructor( // add entries for account to service DB logger.log(Level.INFO, "Writing account configuration to database", config) try { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) val defaultSyncInterval = settingsManager.getLong(Settings.DEFAULT_SYNC_INTERVAL) // Configure CardDAV service @@ -200,7 +202,7 @@ class AccountRepository @Inject constructor( throw IllegalArgumentException("Account with name \"$newName\" already exists") // remember sync intervals - val oldSettings = AccountSettings(context, oldAccount) + val oldSettings = accountSettingsFactory.forAccount(oldAccount) val authorities = mutableListOf( context.getString(R.string.address_books_authority), CalendarContract.AUTHORITY @@ -269,7 +271,7 @@ class AccountRepository @Inject constructor( } // restore sync intervals - val newSettings = AccountSettings(context, newAccount) + val newSettings = accountSettingsFactory.forAccount(newAccount) for ((authority, interval) in syncIntervals) { if (interval == null) ContentResolver.setIsSyncable(newAccount, authority, 0) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt index aeb058696..cd6b78df0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt @@ -49,10 +49,11 @@ import javax.inject.Inject * Implements an observer pattern that can be used to listen for changes of collections. */ class DavCollectionRepository @Inject constructor( + private val accountSettingsFactory: AccountSettings.Factory, @ApplicationContext val context: Context, + db: AppDatabase, defaultListeners: Set<@JvmSuppressWildcards OnChangeListener>, - val serviceRepository: DavServiceRepository, - db: AppDatabase + private val serviceRepository: DavServiceRepository ) { private val listeners = Collections.synchronizedSet(defaultListeners.toMutableSet()) @@ -165,7 +166,7 @@ class DavCollectionRepository @Inject constructor( val service = serviceRepository.get(collection.serviceId) ?: throw IllegalArgumentException("Service not found") val account = Account(service.accountName, context.getString(R.string.account_type)) - HttpClient.Builder(context, AccountSettings(context, account)) + HttpClient.Builder(context, accountSettingsFactory.forAccount(account)) .setForeground(true) .build().use { httpClient -> withContext(Dispatchers.IO) { @@ -250,7 +251,7 @@ class DavCollectionRepository @Inject constructor( // helpers private suspend fun createOnServer(account: Account, url: HttpUrl, method: String, xmlBody: String) { - HttpClient.Builder(context, AccountSettings(context, account)) + HttpClient.Builder(context, accountSettingsFactory.forAccount(account)) .setForeground(true) .build().use { httpClient -> withContext(Dispatchers.IO) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavSyncStatsRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavSyncStatsRepository.kt index dfc755082..f716e13d8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavSyncStatsRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavSyncStatsRepository.kt @@ -4,17 +4,18 @@ package at.bitfire.davdroid.repository -import android.app.Application +import android.content.Context import android.content.pm.PackageManager import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.log.Logger +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import java.text.Collator import javax.inject.Inject class DavSyncStatsRepository @Inject constructor( - val context: Application, + @ApplicationContext val context: Context, db: AppDatabase ) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt index 68c285dfd..58dc3a6ea 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt @@ -31,9 +31,14 @@ import at.bitfire.vcard4android.AndroidContact import at.bitfire.vcard4android.AndroidGroup import at.bitfire.vcard4android.Constants import at.bitfire.vcard4android.GroupMethod +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.EntryPointAccessors +import dagger.hilt.components.SingletonComponent import java.io.ByteArrayOutputStream import java.util.LinkedList import java.util.logging.Level +import javax.inject.Inject /** * A local address book. Requires an own Android account, because Android manages contacts per @@ -41,10 +46,10 @@ import java.util.logging.Level * address book" account for every CardDAV address book. These accounts are bound to a * DAVx5 main account. */ -open class LocalAddressBook( - private val context: Context, - account: Account, - provider: ContentProviderClient? +open class LocalAddressBook @Inject constructor( + private val context: Context, + account: Account, + provider: ContentProviderClient? ): AndroidAddressBook(account, provider, LocalContact.Factory, LocalGroup.Factory), LocalCollection { companion object { @@ -154,6 +159,15 @@ open class LocalAddressBook( } + @EntryPoint + @InstallIn(SingletonComponent::class) + interface LocalAddressBookEntryPoint { + fun accountSettingsFactory(): AccountSettings.Factory + } + private val entryPoint = EntryPointAccessors.fromApplication(context, LocalAddressBookEntryPoint::class.java) + private val accountSettingsFactory = entryPoint.accountSettingsFactory() + + override val tag: String get() = "contacts-${account.name}" @@ -167,7 +181,7 @@ open class LocalAddressBook( * but if it is enabled, [findDirty] will find dirty [LocalContact]s and [LocalGroup]s. */ open val groupMethod: GroupMethod by lazy { - val accountSettings = AccountSettings(context, requireMainAccount()) + val accountSettings = accountSettingsFactory.forAccount(requireMainAccount()) accountSettings.getGroupMethod() } val includeGroups diff --git a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt index 6150816a8..2ef0d0605 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt @@ -74,8 +74,9 @@ import java.util.logging.Level class RefreshCollectionsWorker @AssistedInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, + private val accountSettingsFactory: AccountSettings.Factory, serviceRepository: DavServiceRepository, - val collectionListRefresherFactory: CollectionListRefresher.Factory + private val collectionListRefresherFactory: CollectionListRefresher.Factory ): CoroutineWorker(appContext, workerParams) { companion object { @@ -179,7 +180,7 @@ class RefreshCollectionsWorker @AssistedInject constructor( // create authenticating OkHttpClient (credentials taken from account settings) runInterruptible { - HttpClient.Builder(applicationContext, AccountSettings(applicationContext, account)) + HttpClient.Builder(applicationContext, accountSettingsFactory.forAccount(account)) .setForeground(true) .build().use { client -> val httpClient = client.okHttpClient diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt index 473beea78..4bae6cd7f 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt @@ -21,33 +21,32 @@ import at.bitfire.davdroid.util.setAndVerifyUserData import at.bitfire.davdroid.util.trimToNull import at.bitfire.ical4android.TaskProvider import at.bitfire.vcard4android.GroupMethod -import dagger.hilt.EntryPoint -import dagger.hilt.InstallIn -import dagger.hilt.android.EntryPointAccessors -import dagger.hilt.components.SingletonComponent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ApplicationContext import net.openid.appauth.AuthState import java.util.logging.Level /** * Manages settings of an account. * - * @param context Required to access account settings - * @param argAccount Account to take settings from. If this account is an address book account, + * @param accountOrAddressBookAccount Account to take settings from. If this account is an address book account, * settings will be taken from the corresponding main account instead. * * @throws InvalidAccountException on construction when the account doesn't exist (anymore) * @throws IllegalArgumentException when the account type is not _DAVx5_ or _DAVx5 address book_ */ -class AccountSettings( - val context: Context, - argAccount: Account +class AccountSettings @AssistedInject constructor( + @Assisted accountOrAddressBookAccount: Account, + @ApplicationContext val context: Context, + private val migrationsFactory: AccountSettingsMigrations.Factory, + private val settingsManager: SettingsManager ) { - @EntryPoint - @InstallIn(SingletonComponent::class) - interface AccountSettingsEntryPoint { - fun migrationsFactory(): AccountSettingsMigrations.Factory - fun settingsManager(): SettingsManager + @AssistedFactory + interface Factory { + fun forAccount(account: Account): AccountSettings } companion object { @@ -136,37 +135,34 @@ class AccountSettings( } - private val entryPoint = EntryPointAccessors.fromApplication(context) - private val settings = entryPoint.settingsManager() - val accountManager: AccountManager = AccountManager.get(context) - val account: Account - - init { - account = when (argAccount.type) { + val account: Account = when (accountOrAddressBookAccount.type) { context.getString(R.string.account_type_address_book) -> { - /* argAccount is an address book account, which is not a main account. However settings are - stored in the main account, so resolve and use the main account instead. */ - LocalAddressBook.mainAccount(context, argAccount) ?: throw IllegalArgumentException("Main account of $argAccount not found") + /* argument is an address book account, which is not a main account. However settings are + stored in the main account, so resolve and use the main account instead. */ + LocalAddressBook.mainAccount(context, accountOrAddressBookAccount) ?: throw IllegalArgumentException("Main account of $accountOrAddressBookAccount not found") } context.getString(R.string.account_type) -> - argAccount + accountOrAddressBookAccount else -> - throw IllegalArgumentException("Account type ${argAccount.type} not supported") + throw IllegalArgumentException("Account type ${accountOrAddressBookAccount.type} not supported") } + init { // synchronize because account migration must only be run one time synchronized(AccountSettings::class.java) { - val versionStr = accountManager.getUserData(account, KEY_SETTINGS_VERSION) ?: throw InvalidAccountException(account) + val versionStr = accountManager.getUserData(this.account, KEY_SETTINGS_VERSION) ?: throw InvalidAccountException( + this.account + ) var version = 0 try { version = Integer.parseInt(versionStr) } catch (e: NumberFormatException) { Logger.log.log(Level.SEVERE, "Invalid account version: $versionStr", e) } - Logger.log.fine("Account ${account.name} has version $version, current version: $CURRENT_VERSION") + Logger.log.fine("Account ${this.account.name} has version $version, current version: $CURRENT_VERSION") if (version < CURRENT_VERSION) { if (currentlyUpdating) { @@ -322,8 +318,8 @@ class AccountSettings( } fun getSyncWifiOnly() = - if (settings.containsKey(KEY_WIFI_ONLY)) - settings.getBoolean(KEY_WIFI_ONLY) + if (settingsManager.containsKey(KEY_WIFI_ONLY)) + settingsManager.getBoolean(KEY_WIFI_ONLY) else accountManager.getUserData(account, KEY_WIFI_ONLY) != null @@ -337,8 +333,8 @@ class AccountSettings( fun getSyncWifiOnlySSIDs(): List? = if (getSyncWifiOnly()) { - val strSsids = if (settings.containsKey(KEY_WIFI_ONLY_SSIDS)) - settings.getString(KEY_WIFI_ONLY_SSIDS) + val strSsids = if (settingsManager.containsKey(KEY_WIFI_ONLY_SSIDS)) + settingsManager.getString(KEY_WIFI_ONLY_SSIDS) else accountManager.getUserData(account, KEY_WIFI_ONLY_SSIDS) strSsids?.split(',') @@ -349,7 +345,7 @@ class AccountSettings( fun getIgnoreVpns(): Boolean = when (accountManager.getUserData(account, KEY_IGNORE_VPNS)) { - null -> settings.getBoolean(KEY_IGNORE_VPNS) + null -> settingsManager.getBoolean(KEY_IGNORE_VPNS) "0" -> false else -> true } @@ -410,7 +406,7 @@ class AccountSettings( */ fun getDefaultAlarm() = accountManager.getUserData(account, KEY_DEFAULT_ALARM)?.toInt() ?: - settings.getIntOrNull(KEY_DEFAULT_ALARM)?.takeIf { it != -1 } + settingsManager.getIntOrNull(KEY_DEFAULT_ALARM)?.takeIf { it != -1 } /** * Sets the default alarm value in the local account settings, if the new value differs @@ -423,21 +419,23 @@ class AccountSettings( */ fun setDefaultAlarm(minBefore: Int?) = accountManager.setAndVerifyUserData(account, KEY_DEFAULT_ALARM, - if (minBefore == settings.getIntOrNull(KEY_DEFAULT_ALARM)?.takeIf { it != -1 }) + if (minBefore == settingsManager.getIntOrNull(KEY_DEFAULT_ALARM)?.takeIf { it != -1 }) null else minBefore?.toString()) - fun getManageCalendarColors() = if (settings.containsKey(KEY_MANAGE_CALENDAR_COLORS)) - settings.getBoolean(KEY_MANAGE_CALENDAR_COLORS) - else - accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null + fun getManageCalendarColors() = + if (settingsManager.containsKey(KEY_MANAGE_CALENDAR_COLORS)) + settingsManager.getBoolean(KEY_MANAGE_CALENDAR_COLORS) + else + accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null fun setManageCalendarColors(manage: Boolean) = accountManager.setAndVerifyUserData(account, KEY_MANAGE_CALENDAR_COLORS, if (manage) null else "0") - fun getEventColors() = if (settings.containsKey(KEY_EVENT_COLORS)) - settings.getBoolean(KEY_EVENT_COLORS) - else + fun getEventColors() = + if (settingsManager.containsKey(KEY_EVENT_COLORS)) + settingsManager.getBoolean(KEY_EVENT_COLORS) + else accountManager.getUserData(account, KEY_EVENT_COLORS) != null fun setEventColors(useColors: Boolean) = accountManager.setAndVerifyUserData(account, KEY_EVENT_COLORS, if (useColors) "1" else null) @@ -445,7 +443,7 @@ class AccountSettings( // CardDAV settings fun getGroupMethod(): GroupMethod { - val name = settings.getString(KEY_CONTACT_GROUP_METHOD) ?: + val name = settingsManager.getString(KEY_CONTACT_GROUP_METHOD) ?: accountManager.getUserData(account, KEY_CONTACT_GROUP_METHOD) if (name != null) try { @@ -484,7 +482,7 @@ class AccountSettings( */ @Deprecated("Use getShowOnlyPersonal() instead", replaceWith = ReplaceWith("getShowOnlyPersonal()")) fun getShowOnlyPersonalPair(): Pair = - when (settings.getIntOrNull(KEY_SHOW_ONLY_PERSONAL)) { + when (settingsManager.getIntOrNull(KEY_SHOW_ONLY_PERSONAL)) { 0 -> Pair(false, false) 1 -> Pair(true, false) else /* including -1 */ -> Pair(accountManager.getUserData(account, KEY_SHOW_ONLY_PERSONAL) != null, true) @@ -502,7 +500,6 @@ class AccountSettings( val fromVersion = toVersion-1 Logger.log.info("Updating account ${account.name} from version $fromVersion to $toVersion") try { - val migrationsFactory = entryPoint.migrationsFactory() val migrations = migrationsFactory.create( account = account, accountSettings = this diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettingsMigrations.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettingsMigrations.kt index a73729160..97a52931b 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettingsMigrations.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettingsMigrations.kt @@ -7,10 +7,10 @@ package at.bitfire.davdroid.settings import android.accounts.Account import android.accounts.AccountManager import android.annotation.SuppressLint -import android.app.Application import android.content.ContentResolver import android.content.ContentUris import android.content.ContentValues +import android.content.Context import android.content.pm.PackageManager import android.os.Parcel import android.os.RemoteException @@ -43,6 +43,7 @@ import at.techbee.jtx.JtxContract.asSyncAdapter import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ApplicationContext import net.fortuna.ical4j.model.Property import net.fortuna.ical4j.model.property.Url import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -55,7 +56,7 @@ class AccountSettingsMigrations @AssistedInject constructor( @Assisted val account: Account, @Assisted val accountSettings: AccountSettings, val accountRepository: AccountRepository, - val context: Application, + @ApplicationContext val context: Context, val db: AppDatabase, val settings: SettingsManager ) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt index 487ab0cfe..7d3261b43 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt @@ -32,11 +32,12 @@ import javax.inject.Inject * Sync logic for address books */ class AddressBookSyncer @Inject constructor( + accountSettingsFactory: AccountSettings.Factory, @ApplicationContext context: Context, db: AppDatabase, private val contactsSyncManagerFactory: ContactsSyncManager.Factory, private val settingsManager: SettingsManager -) : Syncer(context, db) { +) : Syncer(accountSettingsFactory, context, db) { companion object { const val PREVIOUS_GROUP_METHOD = "previous_group_method" @@ -134,7 +135,7 @@ class AddressBookSyncer @Inject constructor( collection: Collection ) { try { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) val addressBook = LocalAddressBook(context, account, provider) // handle group method change diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/CalendarSyncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/CalendarSyncer.kt index c780dc550..396404292 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/CalendarSyncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/CalendarSyncer.kt @@ -27,10 +27,11 @@ import javax.inject.Inject * Sync logic for calendars */ class CalendarSyncer @Inject constructor( + accountSettingsFactory: AccountSettings.Factory, @ApplicationContext context: Context, db: AppDatabase, private val calendarSyncManagerFactory: CalendarSyncManager.Factory -): Syncer(context, db) { +): Syncer(accountSettingsFactory, context, db) { override fun sync( account: Account, @@ -42,7 +43,7 @@ class CalendarSyncer @Inject constructor( ) { // 0. preparations - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) if (accountSettings.getEventColors()) AndroidCalendar.insertColors(provider, account) else diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/JtxSyncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/JtxSyncer.kt index 2bde0319a..a4e00ea64 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/JtxSyncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/JtxSyncer.kt @@ -30,10 +30,11 @@ import javax.inject.Inject * Sync logic for jtx board */ class JtxSyncer @Inject constructor( + accountSettingsFactory: AccountSettings.Factory, @ApplicationContext context: Context, db: AppDatabase, private val jtxSyncManagerFactory: JtxSyncManager.Factory -): Syncer(context, db) { +): Syncer(accountSettingsFactory, context, db) { override fun sync( account: Account, @@ -57,7 +58,7 @@ class JtxSyncer @Inject constructor( am.setAccountVisibility(account, TaskProvider.ProviderName.JtxBoard.packageName, AccountManager.VISIBILITY_VISIBLE) } - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) // 1. find jtxCollection collections to be synced val remoteCollections = mutableMapOf() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncAdapterServices.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncAdapterServices.kt index ac12aefca..df61e6de8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncAdapterServices.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncAdapterServices.kt @@ -13,6 +13,7 @@ import android.content.Context import android.content.Intent import android.content.SyncResult import android.os.Bundle +import android.os.IBinder import android.provider.ContactsContract import androidx.work.WorkManager import at.bitfire.davdroid.InvalidAccountException @@ -21,18 +22,31 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.sync.worker.BaseSyncWorker import at.bitfire.davdroid.sync.worker.OneTimeSyncWorker +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.EntryPointAccessors +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import java.util.logging.Level +import javax.inject.Inject abstract class SyncAdapterService: Service() { - fun syncAdapter() = SyncAdapter(this) + @EntryPoint + @InstallIn(SingletonComponent::class) + interface SyncAdapterServiceEntryPoint { + fun syncAdapter(): SyncAdapter + } - override fun onBind(intent: Intent?) = syncAdapter().syncAdapterBinder!! + override fun onBind(intent: Intent?): IBinder { + val entryPoint = EntryPointAccessors.fromApplication(this) + return entryPoint.syncAdapter().syncAdapterBinder + } /** * Entry point for the sync adapter framework. @@ -43,8 +57,9 @@ abstract class SyncAdapterService: Service() { * adapter to provide exported services, which allow android system components and calendar, * contacts or task apps to sync via DAVx5. */ - class SyncAdapter( - context: Context + class SyncAdapter @Inject constructor( + private val accountSettingsFactory: AccountSettings.Factory, + @ApplicationContext context: Context ): AbstractThreadedSyncAdapter( context, true // isSyncable shouldn't be -1 because DAVx5 sets it to 0 or 1. @@ -60,7 +75,7 @@ abstract class SyncAdapterService: Service() { * In any case, the sync framework shouldn't be blocked anymore as soon as a * value is available. */ - val finished = CompletableDeferred() + private val finished = CompletableDeferred() override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { // We seem to have to pass this old SyncFramework extra for an Android 7 workaround @@ -68,7 +83,7 @@ abstract class SyncAdapterService: Service() { Logger.log.info("Sync request via sync framework for $account $authority (upload=$upload)") val accountSettings = try { - AccountSettings(context, account) + accountSettingsFactory.forAccount(account) } catch (e: InvalidAccountException) { Logger.log.log(Level.WARNING, "Account doesn't exist anymore", e) return diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/Syncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/Syncer.kt index 284a7d981..b0c73ebee 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/Syncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/Syncer.kt @@ -27,6 +27,7 @@ import java.util.logging.Level * Also provides useful methods that can be used by derived syncers ie [CalendarSyncer], etc. */ abstract class Syncer( + protected val accountSettingsFactory: AccountSettings.Factory, val context: Context, val db: AppDatabase ) { @@ -77,7 +78,7 @@ abstract class Syncer( else authority - val accountSettings by lazy { AccountSettings(context, account) } + val accountSettings by lazy { accountSettingsFactory.forAccount(account) } val httpClient = lazy { HttpClient.Builder(context, accountSettings).build() } // acquire ContentProviderClient of authority to be synced diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/TaskSyncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/TaskSyncer.kt index 7de40d174..80ef6d412 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/TaskSyncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/TaskSyncer.kt @@ -31,10 +31,11 @@ import javax.inject.Inject * Sync logic for tasks in CalDAV collections ({@code VTODO}). */ class TaskSyncer @Inject constructor( + accountSettingsFactory: AccountSettings.Factory, @ApplicationContext context: Context, db: AppDatabase, private val tasksSyncManagerFactory: TasksSyncManager.Factory -): Syncer(context, db) { +): Syncer(accountSettingsFactory, context, db) { override fun sync( account: Account, @@ -58,7 +59,7 @@ class TaskSyncer @Inject constructor( am.setAccountVisibility(account, providerName.packageName, AccountManager.VISIBILITY_VISIBLE) } - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) // 1. find task collections to be synced val remoteCollections = mutableMapOf() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorker.kt index 3fb8fd2e9..58322be99 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorker.kt @@ -48,12 +48,12 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.withContext import java.util.Collections -import java.util.logging.Level abstract class BaseSyncWorker( context: Context, private val workerParams: WorkerParameters, - private val syncDispatcher: CoroutineDispatcher, + private val accountSettingsFactory: AccountSettings.Factory, + private val syncDispatcher: CoroutineDispatcher ) : CoroutineWorker(context, workerParams) { companion object { @@ -222,7 +222,7 @@ abstract class BaseSyncWorker( try { val accountSettings = try { - AccountSettings(applicationContext, account) + accountSettingsFactory.forAccount(account) } catch (e: InvalidAccountException) { val workId = workerParams.id Logger.log.warning("Account $account doesn't exist anymore, cancelling worker $workId") diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorker.kt index ff4b3727d..57575878a 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorker.kt @@ -24,6 +24,7 @@ import androidx.work.WorkRequest import androidx.work.WorkerParameters import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.sync.SyncDispatcher import at.bitfire.davdroid.sync.SyncUtils import at.bitfire.davdroid.ui.NotificationUtils @@ -43,8 +44,9 @@ import java.util.logging.Level class OneTimeSyncWorker @AssistedInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, + accountSettingsFactory: AccountSettings.Factory, syncDispatcher: SyncDispatcher -) : BaseSyncWorker(appContext, workerParams, syncDispatcher.dispatcher) { +) : BaseSyncWorker(appContext, workerParams, accountSettingsFactory, syncDispatcher.dispatcher) { companion object { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorker.kt index 6a442e7be..6e1771e42 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorker.kt @@ -16,6 +16,7 @@ import androidx.work.Operation import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.sync.SyncDispatcher import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -41,8 +42,9 @@ import java.util.concurrent.TimeUnit class PeriodicSyncWorker @AssistedInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, + accountSettingsFactory: AccountSettings.Factory, syncDispatcher: SyncDispatcher -) : BaseSyncWorker(appContext, workerParams, syncDispatcher.dispatcher) { +) : BaseSyncWorker(appContext, workerParams, accountSettingsFactory, syncDispatcher.dispatcher) { companion object { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt index ed5434826..4a3b751f6 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt @@ -1,7 +1,7 @@ package at.bitfire.davdroid.ui import android.accounts.Account -import android.app.Application +import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager @@ -30,6 +30,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -43,8 +44,8 @@ import java.text.Collator @HiltViewModel(assistedFactory = AccountsModel.Factory::class) class AccountsModel @AssistedInject constructor( @Assisted val syncAccountsOnInit: Boolean, - private val context: Application, private val accountRepository: AccountRepository, + @ApplicationContext val context: Context, private val db: AppDatabase, introPageFactory: IntroPageFactory ): ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt index 2aaf3fe9e..1cdd5e0ec 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt @@ -1,6 +1,6 @@ package at.bitfire.davdroid.ui -import android.app.Application +import android.content.Context import android.content.IntentFilter import android.content.pm.PackageManager import android.os.PowerManager @@ -18,6 +18,7 @@ import at.bitfire.davdroid.util.PermissionUtils import at.bitfire.davdroid.util.TaskUtils import at.bitfire.davdroid.util.broadcastReceiverFlow import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -25,7 +26,7 @@ import javax.inject.Inject @HiltViewModel class AppSettingsModel @Inject constructor( - val context: Application, + @ApplicationContext val context: Context, private val preference: PreferenceRepository, private val settings: SettingsManager ) : ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoModel.kt index ecd42977d..74e300265 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoModel.kt @@ -6,7 +6,6 @@ package at.bitfire.davdroid.ui import android.accounts.Account import android.accounts.AccountManager -import android.app.Application import android.app.usage.UsageStatsManager import android.content.ContentResolver import android.content.ContentUris @@ -56,6 +55,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.dmfs.tasks.contract.TaskContract @@ -73,9 +73,10 @@ import at.techbee.jtx.JtxContract.asSyncAdapter as asJtxSyncAdapter @HiltViewModel(assistedFactory = DebugInfoModel.Factory::class) class DebugInfoModel @AssistedInject constructor( @Assisted private val details: DebugInfoDetails, - val context: Application, - val db: AppDatabase, - val settings: SettingsManager + private val accountSettingsFactory: AccountSettings.Factory, + @ApplicationContext val context: Context, + private val db: AppDatabase, + private val settings: SettingsManager ) : ViewModel() { data class DebugInfoDetails( @@ -474,7 +475,7 @@ class DebugInfoModel @AssistedInject constructor( writer.append("\n\n - Account: ${account.name}\n") writer.append(dumpAccount(account, AccountDumpInfo.mainAccount(context, account))) try { - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) val credentials = accountSettings.credentials() val authStr = mutableListOf() @@ -547,7 +548,7 @@ class DebugInfoModel @AssistedInject constructor( } catch (e: Exception) { nrEntries = e.toString() } - val accountSettings = AccountSettings(context, account) + val accountSettings = accountSettingsFactory.forAccount(account) table.addLine( info.authority, ContentResolver.getIsSyncable(account, info.authority), diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsModel.kt index 124d7541b..c681e2de6 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsModel.kt @@ -1,6 +1,6 @@ package at.bitfire.davdroid.ui -import android.app.Application +import android.content.Context import android.os.Build import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -10,12 +10,13 @@ import androidx.lifecycle.viewModelScope import at.bitfire.davdroid.util.packageChangedFlow import at.bitfire.ical4android.TaskProvider import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class PermissionsModel @Inject constructor( - val context: Application + @ApplicationContext val context: Context, ): ViewModel() { var needKeepPermissions by mutableStateOf(false) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt index dd6f879fb..51b4c0296 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt @@ -1,6 +1,6 @@ package at.bitfire.davdroid.ui -import android.app.Application +import android.content.Context import android.content.pm.PackageManager import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,6 +12,7 @@ import at.bitfire.davdroid.util.TaskUtils import at.bitfire.davdroid.util.packageChangedFlow import at.bitfire.ical4android.TaskProvider import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -19,8 +20,8 @@ import javax.inject.Inject @HiltViewModel class TasksModel @Inject constructor( - val context: Application, - val settings: SettingsManager + @ApplicationContext val context: Context, + private val settings: SettingsManager ) : ViewModel() { companion object { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountProgressUseCase.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountProgressUseCase.kt index e7142a708..7b2c2dfc6 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountProgressUseCase.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountProgressUseCase.kt @@ -5,12 +5,13 @@ package at.bitfire.davdroid.ui.account import android.accounts.Account -import android.app.Application +import android.content.Context import androidx.work.WorkInfo import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker import at.bitfire.davdroid.sync.worker.BaseSyncWorker import at.bitfire.davdroid.sync.worker.OneTimeSyncWorker +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -19,7 +20,7 @@ import kotlinx.coroutines.flow.flowOf import javax.inject.Inject class AccountProgressUseCase @Inject constructor( - val context: Application + @ApplicationContext val context: Context ) { operator fun invoke( diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt index 9dbeaa756..178216c77 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt @@ -5,7 +5,7 @@ package at.bitfire.davdroid.ui.account import android.accounts.Account -import android.app.Application +import android.content.Context import android.provider.CalendarContract import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -30,6 +30,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -44,9 +45,10 @@ import java.util.logging.Level @HiltViewModel(assistedFactory = AccountScreenModel.Factory::class) class AccountScreenModel @AssistedInject constructor( @Assisted val account: Account, - val context: Application, private val accountRepository: AccountRepository, + accountSettingsFactory: AccountSettings.Factory, private val collectionRepository: DavCollectionRepository, + @ApplicationContext val context: Context, serviceRepository: DavServiceRepository, accountProgressUseCase: AccountProgressUseCase, getBindableHomesetsFromService: GetBindableHomeSetsFromServiceUseCase, @@ -63,7 +65,7 @@ class AccountScreenModel @AssistedInject constructor( !accounts.contains(account) } - private val settings = AccountSettings(context, account) + private val settings = accountSettingsFactory.forAccount(account) private val refreshSettingsSignal = MutableLiveData(Unit) val showOnlyPersonal = refreshSettingsSignal.switchMap { object : LiveData() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt index 0103b6871..da893dde6 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt @@ -1,7 +1,7 @@ package at.bitfire.davdroid.ui.account import android.accounts.Account -import android.app.Application +import android.content.Context import android.provider.CalendarContract import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -22,15 +22,17 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @HiltViewModel(assistedFactory = AccountSettingsModel.Factory::class) class AccountSettingsModel @AssistedInject constructor( - val context: Application, - val settings: SettingsManager, - @Assisted val account: Account + @Assisted val account: Account, + private val accountSettingsFactory: AccountSettings.Factory, + @ApplicationContext val context: Context, + private val settings: SettingsManager ): ViewModel(), SettingsManager.OnChangeListener { @AssistedFactory @@ -38,7 +40,7 @@ class AccountSettingsModel @AssistedInject constructor( fun create(account: Account): AccountSettingsModel } - private val accountSettings = AccountSettings(context, account) + private val accountSettings = accountSettingsFactory.forAccount(account) // settings var syncIntervalContacts by mutableStateOf(null) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageModel.kt index bb81d8c15..8df7e93ff 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageModel.kt @@ -1,6 +1,5 @@ package at.bitfire.davdroid.ui.intro -import android.app.Application import android.content.Context import android.content.IntentFilter import android.os.Build @@ -16,13 +15,14 @@ import at.bitfire.davdroid.settings.SettingsManager import at.bitfire.davdroid.util.PermissionUtils import at.bitfire.davdroid.util.broadcastReceiverFlow import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import java.util.Locale import javax.inject.Inject @HiltViewModel class BatteryOptimizationsPageModel @Inject constructor( - val context: Application, + @ApplicationContext val context: Context, private val settings: SettingsManager ): ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/GoogleLoginModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/GoogleLoginModel.kt index 90475bdb6..a87b6b880 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/GoogleLoginModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/GoogleLoginModel.kt @@ -5,7 +5,6 @@ package at.bitfire.davdroid.ui.setup import android.accounts.AccountManager -import android.app.Application import android.content.Context import android.content.Intent import androidx.activity.result.contract.ActivityResultContract @@ -22,6 +21,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationResponse @@ -32,8 +32,8 @@ import java.util.logging.Level @HiltViewModel(assistedFactory = GoogleLoginModel.Factory::class) class GoogleLoginModel @AssistedInject constructor( @Assisted val initialLoginInfo: LoginInfo, - val context: Application, - val authService: AuthorizationService + val authService: AuthorizationService, + @ApplicationContext val context: Context ): ViewModel() { @AssistedFactory diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt index 98da99b4e..ecc0aed98 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt @@ -5,7 +5,7 @@ package at.bitfire.davdroid.ui.setup import android.accounts.Account -import android.app.Application +import android.content.Context import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -22,6 +22,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.SharingStarted @@ -36,9 +37,9 @@ class LoginScreenModel @AssistedInject constructor( @Assisted val initialLoginType: LoginType, @Assisted val skipLoginTypePage: Boolean, @Assisted val initialLoginInfo: LoginInfo, - val context: Application, - val loginTypesProvider: LoginTypesProvider, private val accountRepository: AccountRepository, + @ApplicationContext val context: Context, + val loginTypesProvider: LoginTypesProvider, settingsManager: SettingsManager ): ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/NextcloudLoginModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/NextcloudLoginModel.kt index be5b507d1..9ac855562 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/NextcloudLoginModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/NextcloudLoginModel.kt @@ -4,7 +4,7 @@ package at.bitfire.davdroid.ui.setup -import android.app.Application +import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -16,6 +16,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -24,7 +25,7 @@ import java.util.logging.Level @HiltViewModel(assistedFactory = NextcloudLoginModel.Factory::class) class NextcloudLoginModel @AssistedInject constructor( @Assisted val initialLoginInfo: LoginInfo, - val context: Application + @ApplicationContext val context: Context //val state: SavedStateHandle ): ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/webdav/AddWebdavMountModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/webdav/AddWebdavMountModel.kt index 3b3ecc49c..c5ad8358f 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/webdav/AddWebdavMountModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/webdav/AddWebdavMountModel.kt @@ -4,7 +4,7 @@ package at.bitfire.davdroid.ui.webdav -import android.app.Application +import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -15,13 +15,14 @@ import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Credentials import at.bitfire.davdroid.webdav.WebDavMountRepository import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import javax.inject.Inject @HiltViewModel class AddWebdavMountModel @Inject constructor( - val context: Application, + @ApplicationContext val context: Context, val db: AppDatabase, private val mountRepository: WebDavMountRepository ): ViewModel() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/util/TaskUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/util/TaskUtils.kt index 2876a8145..957533f1c 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/util/TaskUtils.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/util/TaskUtils.kt @@ -22,8 +22,8 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.settings.Settings import at.bitfire.davdroid.settings.SettingsManager -import at.bitfire.davdroid.sync.worker.PeriodicSyncWorker import at.bitfire.davdroid.sync.SyncUtils +import at.bitfire.davdroid.sync.worker.PeriodicSyncWorker import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible import at.bitfire.ical4android.TaskProvider @@ -43,6 +43,7 @@ object TaskUtils { @EntryPoint @InstallIn(SingletonComponent::class) interface TaskUtilsEntryPoint { + fun accountSettingsFactory(): AccountSettings.Factory fun settingsManager(): SettingsManager } @@ -175,7 +176,8 @@ object TaskUtils { private fun setSyncable(context: Context, account: Account, authority: String, syncable: Boolean) { val settingsManager by lazy { EntryPointAccessors.fromApplication(context, SyncUtils.SyncUtilsEntryPoint::class.java).settingsManager() } try { - val settings = AccountSettings(context, account) + val entryPoint = EntryPointAccessors.fromApplication(context) + val settings = entryPoint.accountSettingsFactory().forAccount(account) if (syncable) { Logger.log.info("Enabling $authority sync for $account") diff --git a/app/src/main/kotlin/at/bitfire/davdroid/webdav/WebDavMountRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/webdav/WebDavMountRepository.kt index 8546880cc..f7165903f 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/webdav/WebDavMountRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/webdav/WebDavMountRepository.kt @@ -4,7 +4,7 @@ package at.bitfire.davdroid.webdav -import android.app.Application +import android.content.Context import android.provider.DocumentsContract import androidx.annotation.VisibleForTesting import at.bitfire.dav4jvm.DavResource @@ -13,6 +13,7 @@ import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Credentials import at.bitfire.davdroid.db.WebDavMount import at.bitfire.davdroid.network.HttpClient +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.runInterruptible @@ -21,7 +22,7 @@ import okhttp3.HttpUrl import javax.inject.Inject class WebDavMountRepository @Inject constructor( - val context: Application, + @ApplicationContext val context: Context, val db: AppDatabase ) {