0
0
mirror of https://github.com/thunderbird/thunderbird-android.git synced 2024-09-19 11:42:14 +02:00

Add Glean SDK

This commit is contained in:
cketti 2024-09-10 16:19:39 +02:00
parent f839f4468a
commit 5ed6d8c629
27 changed files with 186 additions and 40 deletions

View File

@ -23,7 +23,7 @@ dependencies {
implementation(projects.feature.widget.messageList)
implementation(projects.feature.widget.shortcut)
implementation(projects.feature.widget.unread)
implementation(projects.feature.telemetry.api)
implementation(projects.feature.telemetry.noop)
implementation(libs.androidx.work.runtime)

View File

@ -7,12 +7,11 @@ import app.k9mail.core.featureflag.FeatureFlagFactory
import app.k9mail.core.ui.theme.api.FeatureThemeProvider
import app.k9mail.core.ui.theme.api.ThemeProvider
import app.k9mail.dev.developmentModuleAdditions
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.telemetryModule
import app.k9mail.feature.widget.shortcut.LauncherShortcutActivity
import app.k9mail.featureflag.K9FeatureFlagFactory
import app.k9mail.provider.K9AppNameProvider
import app.k9mail.provider.K9FeatureThemeProvider
import app.k9mail.telemetry.K9TelemetryManager
import app.k9mail.widget.appWidgetModule
import com.fsck.k9.AppConfig
import com.fsck.k9.BuildConfig
@ -26,6 +25,7 @@ import org.koin.dsl.module
val appModule = module {
includes(appWidgetModule)
includes(telemetryModule)
single(named("ClientInfoAppName")) { BuildConfig.CLIENT_INFO_APP_NAME }
single(named("ClientInfoAppVersion")) { BuildConfig.VERSION_NAME }
@ -35,7 +35,6 @@ val appModule = module {
single<ThemeProvider> { K9ThemeProvider() }
single<FeatureThemeProvider> { K9FeatureThemeProvider() }
single<FeatureFlagFactory> { K9FeatureFlagFactory() }
single<TelemetryManager> { K9TelemetryManager() }
developmentModuleAdditions()
}

View File

@ -1,7 +0,0 @@
package app.k9mail.telemetry
import app.k9mail.feature.telemetry.api.TelemetryManager
class K9TelemetryManager : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = false
}

View File

@ -183,7 +183,11 @@ dependencies {
implementation(projects.feature.widget.messageList)
implementation(projects.feature.widget.shortcut)
implementation(projects.feature.widget.unread)
implementation(projects.feature.telemetry.glean)
debugImplementation(projects.feature.telemetry.noop)
releaseImplementation(projects.feature.telemetry.glean)
"betaImplementation"(projects.feature.telemetry.glean)
"dailyImplementation"(projects.feature.telemetry.glean)
implementation(libs.androidx.work.runtime)

View File

@ -60,6 +60,7 @@ androidx.core:core:1.13.1
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
androidx.databinding:viewbinding:8.4.0
androidx.documentfile:documentfile:1.0.0
androidx.drawerlayout:drawerlayout:1.1.1
androidx.dynamicanimation:dynamicanimation:1.0.0
@ -125,6 +126,7 @@ androidx.versionedparcelable:versionedparcelable:1.1.1
androidx.viewpager2:viewpager2:1.1.0-beta02
androidx.viewpager:viewpager:1.0.0
androidx.window:window:1.0.0
androidx.work:work-runtime-ktx:2.9.1
androidx.work:work-runtime:2.9.1
co.touchlab:stately-concurrency-jvm:2.0.6
co.touchlab:stately-concurrency:2.0.6
@ -151,6 +153,7 @@ com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.mikepenz:materialdrawer:9.0.2
com.squareup.moshi:moshi:1.15.1
com.squareup.okhttp3:okhttp-urlconnection:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.0
com.squareup.okio:okio:3.9.0
@ -171,6 +174,7 @@ io.insert-koin:koin-compose-jvm:1.1.5
io.insert-koin:koin-compose:1.1.5
io.insert-koin:koin-core-jvm:3.5.6
io.insert-koin:koin-core:3.5.6
net.java.dev.jna:jna:5.14.0
net.jcip:jcip-annotations:1.0
net.openid:appauth:0.11.1
org.apache.commons:commons-lang3:3.7
@ -199,6 +203,8 @@ org.jetbrains.kotlinx:kotlinx-datetime:0.6.1
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3
org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.3
org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3
org.jetbrains:annotations:24.1.0
org.jsoup:jsoup:1.17.2
org.minidns:minidns-client:1.0.5
@ -206,4 +212,14 @@ org.minidns:minidns-core:1.0.5
org.minidns:minidns-dnssec:1.0.5
org.minidns:minidns-hla:1.0.5
org.minidns:minidns-iterative-resolver:1.0.5
org.mozilla.components:concept-base:130.0
org.mozilla.components:concept-fetch:130.0
org.mozilla.components:lib-fetch-okhttp:130.0
org.mozilla.components:lib-publicsuffixlist:130.0
org.mozilla.components:service-glean:130.0
org.mozilla.components:support-base:130.0
org.mozilla.components:support-ktx:130.0
org.mozilla.components:support-utils:130.0
org.mozilla.telemetry:glean-native:60.4.0
org.mozilla.telemetry:glean:60.4.0
org.slf4j:slf4j-api:1.7.36

View File

@ -1,8 +1,28 @@
package net.thunderbird.android
import app.k9mail.feature.telemetry.api.TelemetryManager
import com.fsck.k9.CommonApp
import com.fsck.k9.K9
import org.koin.android.ext.android.inject
import org.koin.core.module.Module
class ThunderbirdApp : CommonApp() {
private val telemetryManager: TelemetryManager by inject()
override fun provideAppModule(): Module = appModule
override fun onCreate() {
super.onCreate()
initializeTelemetry()
}
private fun initializeTelemetry() {
telemetryManager.init(
uploadEnabled = K9.isTelemetryEnabled,
releaseChannel = BuildConfig.RELEASE_CHANNEL,
versionCode = BuildConfig.VERSION_CODE,
versionName = BuildConfig.VERSION_NAME,
)
}
}

View File

@ -5,8 +5,7 @@ import app.k9mail.core.common.provider.AppNameProvider
import app.k9mail.core.featureflag.FeatureFlagFactory
import app.k9mail.core.ui.theme.api.FeatureThemeProvider
import app.k9mail.core.ui.theme.api.ThemeProvider
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.glean.GleanTelemetryManager
import app.k9mail.feature.telemetry.telemetryModule
import app.k9mail.feature.widget.shortcut.LauncherShortcutActivity
import com.fsck.k9.AppConfig
import com.fsck.k9.activity.MessageCompose
@ -25,6 +24,7 @@ import org.koin.dsl.module
val appModule = module {
includes(appWidgetModule)
includes(telemetryModule)
single(named("ClientInfoAppName")) { BuildConfig.CLIENT_INFO_APP_NAME }
single(named("ClientInfoAppVersion")) { BuildConfig.VERSION_NAME }
@ -34,7 +34,6 @@ val appModule = module {
single<ThemeProvider> { TbThemeProvider() }
single<FeatureThemeProvider> { TbFeatureThemeProvider() }
single<FeatureFlagFactory> { TbFeatureFlagFactory() }
single<TelemetryManager> { GleanTelemetryManager() }
developmentModuleAdditions()
}

View File

@ -28,8 +28,10 @@ import org.mockito.kotlin.mock
import org.openintents.openpgp.OpenPgpApiManager
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApp::class)
class DependencyInjectionTest : AutoCloseKoinTest() {
private val lifecycleOwner = mock<LifecycleOwner> {
on { lifecycle } doReturn mock()

View File

@ -0,0 +1,9 @@
package net.thunderbird.android
import com.fsck.k9.CommonApp
import org.koin.core.module.Module
// Custom Application class so Glean isn't initialized in tests.
class TestApp : CommonApp() {
override fun provideAppModule(): Module = appModule
}

View File

@ -1,3 +1,4 @@
plugins {
id(ThunderbirdPlugins.Library.jvm)
alias(libs.plugins.android.lint)
}

View File

@ -5,4 +5,14 @@ interface TelemetryManager {
* Returns `true` if the app has a telemetry feature included.
*/
fun isTelemetryFeatureIncluded(): Boolean
/**
* Enable or disable telemetry.
*/
fun setEnabled(enable: Boolean)
/**
* Initialize the telemetry library.
*/
fun init(uploadEnabled: Boolean, releaseChannel: String?, versionCode: Int, versionName: String)
}

View File

@ -9,4 +9,8 @@ android {
dependencies {
api(projects.feature.telemetry.api)
api(libs.okhttp)
implementation(libs.mozilla.components.glean)
implementation(libs.mozilla.components.fetch.okhttp)
}

View File

@ -0,0 +1,15 @@
package app.k9mail.feature.telemetry
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.glean.GleanTelemetryManager
import org.koin.core.module.Module
import org.koin.dsl.module
val telemetryModule: Module = module {
single<TelemetryManager> {
GleanTelemetryManager(
context = get(),
okHttpClient = lazy { get() },
)
}
}

View File

@ -1,7 +1,46 @@
package app.k9mail.feature.telemetry.glean
import android.content.Context
import app.k9mail.feature.telemetry.api.TelemetryManager
import java.util.Calendar
import mozilla.components.lib.fetch.okhttp.OkHttpClient
import mozilla.components.service.glean.net.ConceptFetchHttpUploader
import mozilla.telemetry.glean.BuildInfo
import mozilla.telemetry.glean.Glean
import mozilla.telemetry.glean.config.Configuration
class GleanTelemetryManager : TelemetryManager {
class GleanTelemetryManager(
private val context: Context,
private val okHttpClient: Lazy<okhttp3.OkHttpClient>,
) : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = true
override fun setEnabled(enable: Boolean) {
Glean.setUploadEnabled(enable)
}
override fun init(uploadEnabled: Boolean, releaseChannel: String?, versionCode: Int, versionName: String) {
val httpClient = lazy { OkHttpClient(okHttpClient.value, context) }
val configuration = Configuration(
httpClient = ConceptFetchHttpUploader(httpClient),
channel = releaseChannel,
)
// We don't care for the build date (and including it would make reproducible builds harder).
val buildDate = Calendar.getInstance().apply { clear() }
val buildInfo = BuildInfo(
versionCode = versionCode.toString(),
versionName = versionName,
buildDate = buildDate,
)
Glean.initialize(
context,
uploadEnabled,
configuration,
buildInfo,
)
}
}

View File

@ -0,0 +1,8 @@
plugins {
id(ThunderbirdPlugins.Library.jvm)
alias(libs.plugins.android.lint)
}
dependencies {
api(projects.feature.telemetry.api)
}

View File

@ -0,0 +1,10 @@
package app.k9mail.feature.telemetry
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.noop.NoOpTelemetryManager
import org.koin.core.module.Module
import org.koin.dsl.module
val telemetryModule: Module = module {
single<TelemetryManager> { NoOpTelemetryManager() }
}

View File

@ -0,0 +1,11 @@
package app.k9mail.feature.telemetry.noop
import app.k9mail.feature.telemetry.api.TelemetryManager
class NoOpTelemetryManager : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = false
override fun setEnabled(enable: Boolean) = Unit
override fun init(uploadEnabled: Boolean, releaseChannel: String?, versionCode: Int, versionName: String) = Unit
}

View File

@ -83,6 +83,7 @@ minidns = "1.0.5"
mockito = "5.13.0"
mockitoKotlin = "5.4.0"
moshi = "1.15.1"
mozillaAndroidComponents = "130.0"
okhttp = "4.12.0"
okio = "3.9.0"
preferencesFix = "1.1.0"
@ -215,6 +216,8 @@ mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" }
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
mozilla-components-glean = { module = "org.mozilla.components:service-glean", version.ref = "mozillaAndroidComponents" }
mozilla-components-fetch-okhttp = { module = "org.mozilla.components:lib-fetch-okhttp", version.ref = "mozillaAndroidComponents" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }

View File

@ -37,6 +37,7 @@ dependencies {
testApi(projects.core.testing)
testApi(projects.core.android.testing)
testImplementation(projects.feature.telemetry.noop)
testImplementation(projects.mail.testing)
testImplementation(projects.backend.imap)
testImplementation(projects.mail.protocols.smtp)

View File

@ -2,7 +2,7 @@ package com.fsck.k9
import android.app.Application
import androidx.work.WorkManager
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.telemetryModule
import app.k9mail.legacy.di.DI
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.controller.ControllerExtension
@ -24,7 +24,7 @@ class TestApp : Application() {
super.onCreate()
DI.start(
application = this,
modules = coreModules + storageModule + testModule,
modules = coreModules + storageModule + telemetryModule + testModule,
allowOverride = true,
)
@ -44,9 +44,4 @@ val testModule = module {
single { mock<NotificationStrategy>() }
single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
single { mock<WorkManager>() }
single<TelemetryManager> {
object : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = true
}
}
}

View File

@ -14,7 +14,7 @@ dependencies {
testImplementation(projects.mail.testing)
testImplementation(projects.legacy.testing)
testImplementation(projects.feature.telemetry.api)
testImplementation(projects.feature.telemetry.noop)
testImplementation(libs.robolectric)
testImplementation(libs.commons.io)
}

View File

@ -1,7 +1,7 @@
package com.fsck.k9.storage
import android.app.Application
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.telemetryModule
import app.k9mail.legacy.di.DI
import com.fsck.k9.AppConfig
import com.fsck.k9.Core
@ -20,7 +20,7 @@ class TestApp : Application() {
Core.earlyInit()
super.onCreate()
DI.start(this, coreModules + storageModule + testModule)
DI.start(this, coreModules + storageModule + telemetryModule + testModule)
K9.init(this)
Core.init(this)
@ -33,9 +33,4 @@ val testModule = module {
single { mock<EncryptionExtractor>() }
single<StoragePersister> { K9StoragePersister(get()) }
single { mock<BackendManager>() }
single<TelemetryManager> {
object : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = true
}
}
}

View File

@ -67,6 +67,7 @@ dependencies {
testImplementation(projects.mail.testing)
testImplementation(projects.legacy.storage)
testImplementation(projects.legacy.testing)
testImplementation(projects.feature.telemetry.noop)
testImplementation(libs.robolectric)
testImplementation(libs.androidx.test.core)
testImplementation(libs.kotlin.test)

View File

@ -16,7 +16,14 @@ val settingsUiModule = module {
viewModel { SettingsViewModel(accountManager = get()) }
viewModel { GeneralSettingsViewModel(logFileWriter = get()) }
factory { GeneralSettingsDataStore(jobManager = get(), appLanguageManager = get(), generalSettingsManager = get()) }
factory {
GeneralSettingsDataStore(
jobManager = get(),
appLanguageManager = get(),
generalSettingsManager = get(),
telemetryManager = get(),
)
}
single(named("SaveSettingsExecutorService")) {
Executors.newSingleThreadExecutor(NamedThreadFactory("SaveSettings"))
}

View File

@ -1,6 +1,7 @@
package com.fsck.k9.ui.settings.general
import androidx.preference.PreferenceDataStore
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.legacy.preferences.AppTheme
import app.k9mail.legacy.preferences.GeneralSettingsManager
import app.k9mail.legacy.preferences.SubTheme
@ -16,6 +17,7 @@ class GeneralSettingsDataStore(
private val jobManager: K9JobManager,
private val appLanguageManager: AppLanguageManager,
private val generalSettingsManager: GeneralSettingsManager,
private val telemetryManager: TelemetryManager,
) : PreferenceDataStore() {
private var skipSaveSettings = false
@ -75,7 +77,7 @@ class GeneralSettingsDataStore(
"debug_logging" -> K9.isDebugLoggingEnabled = value
"sensitive_logging" -> K9.isSensitiveDebugLoggingEnabled = value
"volume_navigation" -> K9.isUseVolumeKeysForNavigation = value
"enable_telemetry" -> K9.isTelemetryEnabled = value
"enable_telemetry" -> setTelemetryEnabled(value)
else -> return
}
@ -307,4 +309,9 @@ class GeneralSettingsDataStore(
"move" -> SwipeAction.Move
else -> throw AssertionError()
}
private fun setTelemetryEnabled(enable: Boolean) {
K9.isTelemetryEnabled = enable
telemetryManager.setEnabled(enable)
}
}

View File

@ -1,7 +1,7 @@
package com.fsck.k9
import android.app.Application
import app.k9mail.feature.telemetry.api.TelemetryManager
import app.k9mail.feature.telemetry.telemetryModule
import app.k9mail.legacy.di.DI
import com.fsck.k9.preferences.InMemoryStoragePersister
import com.fsck.k9.preferences.StoragePersister
@ -14,7 +14,7 @@ class TestApp : Application() {
super.onCreate()
DI.start(
application = this,
modules = coreModules + commonAppModules + uiModules + testModule,
modules = coreModules + commonAppModules + uiModules + telemetryModule + testModule,
allowOverride = true,
)
@ -27,9 +27,4 @@ val testModule = module {
single { AppConfig(emptyList()) }
single<CoreResourceProvider> { TestCoreResourceProvider() }
single<StoragePersister> { InMemoryStoragePersister() }
single<TelemetryManager> {
object : TelemetryManager {
override fun isTelemetryFeatureIncluded(): Boolean = true
}
}
}

View File

@ -12,6 +12,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven(url = "https://maven.mozilla.org/maven2")
maven(url = "https://jitpack.io")
}
}
@ -77,6 +78,7 @@ include(
include(
":feature:telemetry:api",
":feature:telemetry:noop",
":feature:telemetry:glean",
)