-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Attributed metrics module and skeleton #6887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 8 commits
c611087
364376d
0283042
77a7734
92351f7
25a430f
60761d1
c8c63fb
1e2a8db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
plugins { | ||
id 'java-library' | ||
id 'kotlin' | ||
} | ||
|
||
apply from: "$rootProject.projectDir/code-formatting.gradle" | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_17 | ||
targetCompatibility = JavaVersion.VERSION_17 | ||
} | ||
|
||
kotlin { | ||
jvmToolchain(17) | ||
} | ||
|
||
dependencies { | ||
implementation Kotlin.stdlib.jdk7 | ||
implementation KotlinX.coroutines.core | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright (c) 2025 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.attributed.metrics.api | ||
|
||
/** | ||
* Client for collecting and emitting attributed metrics. | ||
*/ | ||
interface AttributedMetricClient { | ||
/** | ||
* Stores an event occurrence for later analysis. | ||
* Does nothing if the client is not active. | ||
* | ||
* @param eventName Name of the event to collect | ||
*/ | ||
fun collectEvent(eventName: String) | ||
|
||
/** | ||
* Calculates statistics for a specific event over a time period. | ||
* Returns zero stats if the client is not active. | ||
* | ||
* @param eventName Name of the event to analyze | ||
* @param days Number of days to look back | ||
* @return Statistics about the event's occurrences | ||
*/ | ||
suspend fun getEventStats( | ||
eventName: String, | ||
days: Int, | ||
): EventStats | ||
|
||
/** | ||
* Emits a metric with its parameters if the client is active. | ||
* Does nothing if the client is not active. | ||
* | ||
* @param metric The metric to emit | ||
*/ | ||
fun emitMetric(metric: AttributedMetric) | ||
} | ||
|
||
/** | ||
* Statistics about collected events over a time period. | ||
* | ||
* @property daysWithEvents Number of days that had at least one event | ||
* @property rollingAverage Average number of events per day over the period | ||
* @property totalEvents Total number of events in the period | ||
*/ | ||
data class EventStats( | ||
val daysWithEvents: Int, | ||
val rollingAverage: Double, | ||
val totalEvents: Int, | ||
) | ||
|
||
/** | ||
* Interface for defining an attributed metric. | ||
* Each metric implementation should provide its name and parameters. | ||
*/ | ||
interface AttributedMetric { | ||
/** | ||
* @return The name used to identify this metric | ||
*/ | ||
fun getPixelName(): String | ||
|
||
/** | ||
* @return Parameters to be included with this metric | ||
*/ | ||
suspend fun getMetricParameters(): Map<String, String> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
plugins { | ||
id 'com.android.library' | ||
id 'kotlin-android' | ||
id 'com.google.devtools.ksp' | ||
id 'com.squareup.anvil' | ||
} | ||
|
||
apply from: "$rootProject.projectDir/gradle/android-library.gradle" | ||
|
||
dependencies { | ||
anvil project(path: ':anvil-compiler') | ||
|
||
implementation project(path: ':anvil-annotations') | ||
implementation project(path: ':attributed-metrics-api') | ||
implementation project(path: ':common-utils') | ||
implementation project(path: ':di') | ||
implementation project(path: ':app-build-config-api') | ||
implementation project(path: ':statistics-api') | ||
|
||
implementation KotlinX.coroutines.core | ||
implementation KotlinX.coroutines.android | ||
|
||
implementation Google.dagger | ||
|
||
// DataStore | ||
api AndroidX.dataStore.preferences | ||
|
||
// Room | ||
implementation AndroidX.room.ktx | ||
ksp AndroidX.room.compiler | ||
|
||
implementation "com.squareup.logcat:logcat:_" | ||
|
||
implementation AndroidX.core.ktx | ||
|
||
testImplementation Testing.junit4 | ||
testImplementation "org.mockito.kotlin:mockito-kotlin:_" | ||
testImplementation "androidx.lifecycle:lifecycle-runtime-testing:_" | ||
testImplementation project(path: ':common-test') | ||
testImplementation project(':data-store-test') | ||
testImplementation project(':feature-toggles-test') | ||
testImplementation CashApp.turbine | ||
testImplementation Testing.robolectric | ||
testImplementation(KotlinX.coroutines.test) { | ||
// https://github.com/Kotlin/kotlinx.coroutines/issues/2023 | ||
// conflicts with mockito due to direct inclusion of byte buddy | ||
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug" | ||
} | ||
testImplementation AndroidX.test.ext.junit | ||
testImplementation AndroidX.archCore.testing | ||
testImplementation AndroidX.room.testing | ||
testImplementation AndroidX.room.rxJava2 | ||
|
||
androidTestImplementation AndroidX.test.runner | ||
androidTestImplementation AndroidX.test.rules | ||
|
||
coreLibraryDesugaring Android.tools.desugarJdkLibs | ||
} | ||
|
||
android { | ||
anvil { | ||
generateDaggerFactories = true // default is false | ||
} | ||
lintOptions { | ||
baseline file("lint-baseline.xml") | ||
abortOnError = !project.hasProperty("abortOnError") || project.property("abortOnError") != "false" | ||
} | ||
namespace 'com.duckduckgo.app.attributed.metrics' | ||
compileOptions { | ||
coreLibraryDesugaringEnabled = true | ||
} | ||
buildFeatures { | ||
buildConfig = true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I don't think we need the baseline file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was needed, lint failed and I had to create the baseline. |
||
<issues format="6" by="lint 8.8.2" type="baseline" client="gradle" dependencies="false" name="AGP (8.7.3)" variant="all" version="8.8.2"> | ||
|
||
</issues> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright (c) 2025 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.attributed.metrics | ||
|
||
import com.duckduckgo.anvil.annotations.ContributesRemoteFeature | ||
import com.duckduckgo.di.scopes.AppScope | ||
import com.duckduckgo.feature.toggles.api.Toggle | ||
import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue | ||
|
||
@ContributesRemoteFeature( | ||
scope = AppScope::class, | ||
featureName = "attributedMetrics", | ||
) | ||
interface AttributedMetricsConfigFeature { | ||
@Toggle.DefaultValue(DefaultFeatureValue.INTERNAL) | ||
fun self(): Toggle | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.attributed.metrics | ||
|
||
internal interface AttributedMetricsState { | ||
|
||
fun isEnabled(): Boolean | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.attributed.metrics.di | ||
|
||
import android.content.Context | ||
import androidx.datastore.core.DataStore | ||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory | ||
import androidx.datastore.preferences.core.Preferences | ||
import androidx.datastore.preferences.preferencesDataStoreFile | ||
import com.duckduckgo.di.scopes.AppScope | ||
import com.squareup.anvil.annotations.ContributesTo | ||
import dagger.Module | ||
import dagger.Provides | ||
import dagger.SingleInstanceIn | ||
import javax.inject.Qualifier | ||
|
||
@Module | ||
@ContributesTo(AppScope::class) | ||
object AttributedMetricsDataStoreModule { | ||
@Provides | ||
@SingleInstanceIn(AppScope::class) | ||
@AttributedMetrics | ||
fun provideDataStore(context: Context): DataStore<Preferences> = | ||
PreferenceDataStoreFactory.create( | ||
produceFile = { context.preferencesDataStoreFile("attributed_metrics_v1") }, | ||
) | ||
} | ||
|
||
@Qualifier | ||
@Retention(AnnotationRetention.BINARY) | ||
annotation class AttributedMetrics |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.attributed.metrics.di | ||
|
||
import android.content.Context | ||
import androidx.room.Room | ||
import com.duckduckgo.app.attributed.metrics.store.AttributedMetricsDatabase | ||
import com.duckduckgo.app.attributed.metrics.store.EventDao | ||
import com.duckduckgo.di.scopes.AppScope | ||
import com.squareup.anvil.annotations.ContributesTo | ||
import dagger.Module | ||
import dagger.Provides | ||
import dagger.SingleInstanceIn | ||
|
||
@Module | ||
@ContributesTo(AppScope::class) | ||
class AttributedMetricsDatabaseModule { | ||
@Provides | ||
@SingleInstanceIn(AppScope::class) | ||
fun provideAttributedMetricsDatabase(context: Context): AttributedMetricsDatabase = | ||
Room | ||
.databaseBuilder( | ||
context = context, | ||
klass = AttributedMetricsDatabase::class.java, | ||
name = "attributed_metrics.db", | ||
).fallbackToDestructiveMigration() | ||
.build() | ||
|
||
@Provides | ||
@SingleInstanceIn(AppScope::class) | ||
fun provideEventDao(db: AttributedMetricsDatabase): EventDao = db.eventDao() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: don't think we need this since there are no linting issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto