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

Commit 7d468996 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Refactor Settings provider

* don't use a separate :sync process anymore, so that settings management doesn't need IPC
* remove Settings service and IPC, use singleton with application Context instead
* adapt default number of sync worker threads
* library updates
parent b863d355
Loading
Loading
Loading
Loading

Settings.kt

0 → 100644
+185 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright © Ricki Hirner (bitfire web engineering).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */

package at.bitfire.davdroid.settings

import android.content.Context
import at.bitfire.davdroid.log.Logger
import java.lang.ref.WeakReference
import java.util.*
import java.util.logging.Level

class Settings(
        appContext: Context
) {

    companion object {

        // settings keys and default values
        const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs"
        const val DISTRUST_SYSTEM_CERTIFICATES_DEFAULT = false
        const val OVERRIDE_PROXY = "override_proxy"
        const val OVERRIDE_PROXY_DEFAULT = false
        const val OVERRIDE_PROXY_HOST = "override_proxy_host"
        const val OVERRIDE_PROXY_PORT = "override_proxy_port"

        const val OVERRIDE_PROXY_HOST_DEFAULT = "localhost"
        const val OVERRIDE_PROXY_PORT_DEFAULT = 8118


        private var singleton: Settings? = null

        fun getInstance(context: Context): Settings {
            singleton?.let { return it }

            val newInstance = Settings(context.applicationContext)
            singleton = newInstance
            return newInstance
        }

    }

    private val providers = LinkedList<SettingsProvider>()
    private val observers = LinkedList<WeakReference<OnChangeListener>>()

    init {
        ServiceLoader.load(ISettingsProviderFactory::class.java).forEach { factory ->
            providers.addAll(factory.getProviders(appContext))
        }
    }

    fun forceReload() {
        providers.forEach {
            it.forceReload()
        }
        onSettingsChanged()
    }


    /*** OBSERVERS ***/

    fun addOnChangeListener(observer: OnChangeListener) {
        observers += WeakReference(observer)
    }

    fun removeOnChangeListener(observer: OnChangeListener) {
        observers.removeAll { it.get() == null || it.get() == observer }
    }

    fun onSettingsChanged() {
        observers.mapNotNull { it.get() }.forEach {
            it.onSettingsChanged()
        }
    }


    /*** SETTINGS ACCESS ***/

    fun has(key: String): Boolean {
        Logger.log.fine("Looking for setting $key")
        var result = false
        for (provider in providers)
            try {
                val (value, further) = provider.has(key)
                Logger.log.finer("${provider::class.java.simpleName}: has $key = $value, continue: $further")
                if (value) {
                    result = true
                    break
                }
                if (!further)
                    break
            } catch(e: Exception) {
                Logger.log.log(Level.SEVERE, "Couldn't look up setting in $provider", e)
            }
        Logger.log.fine("Looking for setting $key -> $result")
        return result
    }

    private fun<T> getValue(key: String, reader: (SettingsProvider) -> Pair<T?, Boolean>): T? {
        Logger.log.fine("Looking up setting $key")
        var result: T? = null
        for (provider in providers)
            try {
                val (value, further) = reader(provider)
                Logger.log.finer("${provider::class.java.simpleName}: value = $value, continue: $further")
                value?.let { result = it }
                if (!further)
                    break
            } catch(e: Exception) {
                Logger.log.log(Level.SEVERE, "Couldn't read setting from $provider", e)
            }
        Logger.log.fine("Looked up setting $key -> $result")
        return result
    }

    fun getBoolean(key: String) =
            getValue(key) { provider -> provider.getBoolean(key) }

    fun getInt(key: String) =
            getValue(key) { provider -> provider.getInt(key) }

    fun getLong(key: String) =
            getValue(key) { provider -> provider.getLong(key) }

    fun getString(key: String) =
            getValue(key) { provider -> provider.getString(key) }


    fun isWritable(key: String): Boolean {
        for (provider in providers) {
            val (value, further) = provider.isWritable(key)
            if (value)
                return true
            if (!further)
                return false
        }
        return false
    }

    private fun<T> putValue(key: String, value: T?, writer: (SettingsProvider) -> Boolean): Boolean {
        Logger.log.fine("Trying to write setting $key = $value")
        for (provider in providers) {
            val (writable, further) = provider.isWritable(key)
            Logger.log.finer("${provider::class.java.simpleName}: writable = $writable, continue: $further")
            if (writable)
                return try {
                    writer(provider)
                } catch (e: Exception) {
                    Logger.log.log(Level.SEVERE, "Couldn't write setting to $provider", e)
                    false
                }
            if (!further)
                return false
        }
        return false
    }

    fun putBoolean(key: String, value: Boolean?) =
            putValue(key, value) { provider -> provider.putBoolean(key, value) }

    fun putInt(key: String, value: Int?) =
            putValue(key, value) { provider -> provider.putInt(key, value) }

    fun putLong(key: String, value: Long?) =
            putValue(key, value) { provider -> provider.putLong(key, value) }

    fun putString(key: String, value: String?) =
            putValue(key, value) { provider -> provider.putString(key, value) }

    fun remove(key: String): Boolean {
        var deleted = false
        providers.forEach { deleted = deleted || it.remove(key) }
        return deleted
    }


    interface OnChangeListener {
        fun onSettingsChanged()
    }

}
+3 −4
Original line number Original line Diff line number Diff line
@@ -33,7 +33,6 @@ android {
    productFlavors {
    productFlavors {
        standard {
        standard {
            versionName "2.0.7-ose"
            versionName "2.0.7-ose"
            buildConfigField "boolean", "customCerts", "true"
        }
        }
    }
    }


@@ -83,7 +82,7 @@ dependencies {
    implementation 'com.github.yukuku:ambilwarna:2.0.1'
    implementation 'com.github.yukuku:ambilwarna:2.0.1'
    implementation 'com.mikepenz:aboutlibraries:6.2.0'
    implementation 'com.mikepenz:aboutlibraries:6.2.0'


    implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
    implementation 'commons-io:commons-io:2.6'
    implementation 'commons-io:commons-io:2.6'
    implementation 'dnsjava:dnsjava:2.1.8'
    implementation 'dnsjava:dnsjava:2.1.8'
    implementation 'org.apache.commons:commons-lang3:3.8.1'
    implementation 'org.apache.commons:commons-lang3:3.8.1'
@@ -93,8 +92,8 @@ dependencies {
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test:rules:1.1.1'
    androidTestImplementation 'androidx.test:rules:1.1.1'
    androidTestImplementation 'junit:junit:4.12'
    androidTestImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0'
    androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'


    testImplementation 'junit:junit:4.12'
    testImplementation 'junit:junit:4.12'
    testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0'
    testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
}
}
+8 −9
Original line number Original line Diff line number Diff line
@@ -8,32 +8,31 @@


package at.bitfire.davdroid.settings
package at.bitfire.davdroid.settings


import at.bitfire.davdroid.App
import org.junit.Assert.assertEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertFalse
import org.junit.Test
import org.junit.Test


class DefaultsProviderTest {
class DefaultsSettingsProviderTest {


    private val provider: Provider = DefaultsProvider()
    private val provider: SettingsProvider = DefaultsProvider()


    @Test
    @Test
    fun testHas() {
    fun testHas() {
        assertEquals(Pair(false, true), provider.has("notExisting"))
        assertEquals(Pair(false, true), provider.has("notExisting"))
        assertEquals(Pair(true, true), provider.has(App.OVERRIDE_PROXY))
        assertEquals(Pair(true, true), provider.has(Settings.OVERRIDE_PROXY))
    }
    }


    @Test
    @Test
    fun testGet() {
    fun testGet() {
        assertEquals(Pair("localhost", true), provider.getString(App.OVERRIDE_PROXY_HOST))
        assertEquals(Pair("localhost", true), provider.getString(Settings.OVERRIDE_PROXY_HOST))
        assertEquals(Pair(8118, true), provider.getInt(App.OVERRIDE_PROXY_PORT))
        assertEquals(Pair(8118, true), provider.getInt(Settings.OVERRIDE_PROXY_PORT))
    }
    }


    @Test
    @Test
    fun testPutRemove() {
    fun testPutRemove() {
        assertEquals(Pair(false, true), provider.isWritable(App.OVERRIDE_PROXY))
        assertEquals(Pair(false, true), provider.isWritable(Settings.OVERRIDE_PROXY))
        assertFalse(provider.putBoolean(App.OVERRIDE_PROXY, true))
        assertFalse(provider.putBoolean(Settings.OVERRIDE_PROXY, true))
        assertFalse(provider.remove(App.OVERRIDE_PROXY))
        assertFalse(provider.remove(Settings.OVERRIDE_PROXY))
    }
    }


}
}
 No newline at end of file
+4 −12
Original line number Original line Diff line number Diff line
@@ -9,8 +9,6 @@
package at.bitfire.davdroid.settings
package at.bitfire.davdroid.settings


import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.davdroid.App
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Before
@@ -18,25 +16,19 @@ import org.junit.Test


class SettingsTest {
class SettingsTest {


    lateinit var settings: Settings.Stub
    lateinit var settings: Settings


    @Before
    @Before
    fun init() {
    fun initialize() {
        settings = Settings.getInstance(InstrumentationRegistry.getInstrumentation().targetContext)!!
        settings = Settings.getInstance(InstrumentationRegistry.getInstrumentation().targetContext)
    }
    }


    @After
    fun shutdown() {
        settings.close()
    }


    @Test
    @Test
    fun testHas() {
    fun testHas() {
        assertFalse(settings.has("notExisting"))
        assertFalse(settings.has("notExisting"))


        // provided by DefaultsProvider
        // provided by DefaultsProvider
        assertTrue(settings.has(App.OVERRIDE_PROXY))
        assertTrue(settings.has(Settings.OVERRIDE_PROXY))
    }
    }


}
}
 No newline at end of file
+0 −5
Original line number Original line Diff line number Diff line
@@ -57,7 +57,6 @@
        tools:ignore="UnusedAttribute">
        tools:ignore="UnusedAttribute">


        <service android:name=".DavService"/>
        <service android:name=".DavService"/>
        <service android:name=".settings.Settings"/>


        <activity
        <activity
            android:name=".ui.AccountsActivity"
            android:name=".ui.AccountsActivity"
@@ -130,7 +129,6 @@
        <service
        <service
            android:name=".syncadapter.CalendarsSyncAdapterService"
            android:name=".syncadapter.CalendarsSyncAdapterService"
            android:exported="true"
            android:exported="true"
            android:process=":sync"
            tools:ignore="ExportedService">
            tools:ignore="ExportedService">
            <intent-filter>
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
                <action android:name="android.content.SyncAdapter"/>
@@ -143,7 +141,6 @@
        <service
        <service
            android:name=".syncadapter.TasksSyncAdapterService"
            android:name=".syncadapter.TasksSyncAdapterService"
            android:exported="true"
            android:exported="true"
            android:process=":sync"
            tools:ignore="ExportedService">
            tools:ignore="ExportedService">
            <intent-filter>
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
                <action android:name="android.content.SyncAdapter"/>
@@ -175,7 +172,6 @@
        <service
        <service
            android:name=".syncadapter.AddressBooksSyncAdapterService"
            android:name=".syncadapter.AddressBooksSyncAdapterService"
            android:exported="true"
            android:exported="true"
            android:process=":sync"
            tools:ignore="ExportedService">
            tools:ignore="ExportedService">
            <intent-filter>
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
                <action android:name="android.content.SyncAdapter"/>
@@ -188,7 +184,6 @@
        <service
        <service
            android:name=".syncadapter.ContactsSyncAdapterService"
            android:name=".syncadapter.ContactsSyncAdapterService"
            android:exported="true"
            android:exported="true"
            android:process=":sync"
            tools:ignore="ExportedService">
            tools:ignore="ExportedService">
            <intent-filter>
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
                <action android:name="android.content.SyncAdapter"/>
Loading