diff --git a/android/build.gradle b/android/build.gradle index f2ac34169..a9d1dc79e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -62,9 +62,6 @@ dependencies { // implementation 'androidx.appcompat:appcompat:1.3.1' // implementation 'com.google.android.material:material:1.4.0' //dependency on work manager - //user needs to provide dependency, in case work manager is used - compileOnly deps.work - //core kotlin project api project(path: projects.core) api project(path: projects.models) @@ -75,6 +72,7 @@ dependencies { compileOnly project(path: projects.json_rudder_adapter) //work manager + //user needs to provide dependency, in case work manager is used compileOnly deps.work compileOnly deps.workMultiprocess diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/DataSinkWorkManagerExtensions.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/DataSinkWorkManagerExtensions.kt deleted file mode 100644 index 1fafef93b..000000000 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/DataSinkWorkManagerExtensions.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Creator: Debanjan Chatterjee on 23/11/23, 6:20 pm Last modified: 21/11/23, 5:14 pm - * Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com - * - * 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.rudderstack.android.internal.infrastructure.sync - -import android.app.Application -import androidx.work.Configuration -import androidx.work.Constraints -import androidx.work.Data -import androidx.work.ExistingPeriodicWorkPolicy -import androidx.work.NetworkType -import androidx.work.PeriodicWorkRequestBuilder -import androidx.work.WorkManager -import androidx.work.multiprocess.RemoteWorkManager -import com.rudderstack.android.currentConfigurationAndroid -import com.rudderstack.core.Analytics -import java.lang.ref.WeakReference -import java.util.concurrent.TimeUnit - -/** - * Aids in data syncing to server with help of work manager. - * Only works if work manager dependency is added externally - * Also, if multi-process support is needed, set the default process name. - * For multi-process support, the following dependency is expected - * Implementation "androidx.work:work-multiprocess:2.5.x" - */ - -private var analyticsRef: WeakReference? = null -private const val WORK_MANAGER_TAG = "rudder_sink" -private const val WORK_NAME = "rudder_sink_work" -private const val REPEAT_INTERVAL_IN_MINS = 15L - -internal val Application.sinkAnalytics - get() = analyticsRef?.get()?.takeUnless { it.isShutdown } - - -private val constraints by lazy { - Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() -} -private fun sinkWorker(workManagerAnalyticsFactoryClassName: Class) = - PeriodicWorkRequestBuilder( - REPEAT_INTERVAL_IN_MINS, TimeUnit.MINUTES - ).setInitialDelay(REPEAT_INTERVAL_IN_MINS, TimeUnit.MINUTES).setConstraints(constraints) - .setInputData(Data.Builder().putString( - RudderSyncWorker.WORKER_ANALYTICS_FACTORY_KEY, - workManagerAnalyticsFactoryClassName.name - ).build()) - .addTag(WORK_MANAGER_TAG).build() - -//<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>> -internal fun Application.registerWorkManager( - analytics: Analytics, workManagerAnalyticsFactoryClass: Class -) { - analytics.logger.debug(log = "Initializing work manager") - //if analytics object has changed, shutting it down is not this method's responsibility - analyticsRef = WeakReference(analytics) - - Configuration.Builder().also { - // if process name is available, this is a multi-process app - analytics.currentConfigurationAndroid?.defaultProcessName?.apply { - it.setDefaultProcessName(this) - } - analytics.currentConfigurationAndroid?.networkExecutor?.apply { - it.setExecutor(this) - } - }.let { - WorkManager.initialize(this, it.build()) - } - if (analytics.currentConfigurationAndroid?.multiProcessEnabled == true) { - RemoteWorkManager.getInstance(this).enqueueUniquePeriodicWork( - WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, sinkWorker(workManagerAnalyticsFactoryClass) - ) - } else WorkManager.getInstance(this).enqueueUniquePeriodicWork( - WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, sinkWorker(workManagerAnalyticsFactoryClass) - ) - -} -internal fun Application.unregisterWorkManager() { - analyticsRef?.clear() - analyticsRef = null - WorkManager.getInstance(this).cancelAllWorkByTag(WORK_MANAGER_TAG) - RemoteWorkManager.getInstance(this).cancelAllWorkByTag(WORK_MANAGER_TAG) -} diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/RudderSyncWorker.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/RudderSyncWorker.kt deleted file mode 100644 index 374acf687..000000000 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/RudderSyncWorker.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Creator: Debanjan Chatterjee on 27/11/23, 2:51 pm Last modified: 23/11/23, 6:21 pm - * Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com - * - * 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.rudderstack.android.internal.infrastructure.sync - -import android.app.Application -import android.content.Context -import androidx.work.Worker -import androidx.work.WorkerParameters -import com.rudderstack.core.Analytics - -//import androidx.work.Worker -//import androidx.work.WorkerParameters - -/** - * Syncs the data at an interval with rudder server - * - */ -internal class RudderSyncWorker(appContext: Context, workerParams: WorkerParameters -) : - Worker(appContext, workerParams) { - companion object{ - internal const val WORKER_ANALYTICS_FACTORY_KEY = "WORKER_ANALYTICS_FACTORY_KEY" - } - override fun doWork(): Result { - - (applicationContext as? Application)?.let { - val weakSinkAnalytics = it.sinkAnalytics - val sinkAnalytics = (weakSinkAnalytics?:createSinkAnalytics()) - val success = sinkAnalytics?.blockingFlush() - sinkAnalytics?.logger?.debug(log = "Data upload through worker. success: $success") - if(weakSinkAnalytics == null) - sinkAnalytics?.shutdown() - return if(success == true) Result.success() else Result.failure() - } - return Result.failure() - } - - private fun createSinkAnalytics(): Analytics? { - val analyticsFactoryClassName = inputData.getString(WORKER_ANALYTICS_FACTORY_KEY) - return analyticsFactoryClassName?.let { - val analyticsFactory = Class.forName(it).newInstance() as WorkManagerAnalyticsFactory - analyticsFactory.createAnalytics(applicationContext as Application) - } - - } - -} \ No newline at end of file diff --git a/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt b/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt index b5c563727..574a58e86 100644 --- a/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt +++ b/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt @@ -78,7 +78,6 @@ abstract class AnalyticsTest { @Before fun setup() { - println("Setup called") mockedControlPlane = mock(ConfigDownloadService::class.java).also { `when`(it.download(any())).then { @@ -94,7 +93,6 @@ abstract class AnalyticsTest { mockedDataUploadService.let { whenever(it.upload(any(), any(), any())).then { -// storage.deleteMessages(data) it.getArgument<(response: HttpResponse) -> Unit>(2).invoke( mockedResponse ) @@ -213,7 +211,7 @@ abstract class AnalyticsTest { hasProperty("userId", `is`("user_id")), hasProperty( "context", allOf( - aMapWithSize(3), hasEntry( + hasEntry( equalTo("traits"), allOf( notNullValue(), aMapWithSize(3), diff --git a/dependencies.gradle b/dependencies.gradle index d14b1309b..928fc51e9 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -14,7 +14,7 @@ ext { versions = [ - work : "2.7.0", + work : "2.9.0", gson : "2.8.8", jackson : "2.12.4", jacksonKotlin : "2.13.3", diff --git a/libs/navigationplugin/build.gradle.kts b/libs/navigationplugin/build.gradle.kts index cb1859446..3d8dd2b35 100644 --- a/libs/navigationplugin/build.gradle.kts +++ b/libs/navigationplugin/build.gradle.kts @@ -63,3 +63,6 @@ dependencies { tasks.withType(type = org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs::class) { kotlinOptions.jvmTarget = "17" } +apply(from = "${project.projectDir.parentFile.parent}/gradle/artifacts-aar.gradle") +apply(from = "${project.projectDir.parentFile.parent}/gradle/mvn-publish.gradle") +apply(from = "${project.projectDir.parentFile.parent}/gradle/codecov.gradle") diff --git a/libs/navigationplugin/config.properties b/libs/navigationplugin/config.properties new file mode 100644 index 000000000..554c2420d --- /dev/null +++ b/libs/navigationplugin/config.properties @@ -0,0 +1 @@ +platform="java-kotlin" diff --git a/libs/navigationplugin/gradle.properties b/libs/navigationplugin/gradle.properties index 557db7b0c..a3f27135f 100644 --- a/libs/navigationplugin/gradle.properties +++ b/libs/navigationplugin/gradle.properties @@ -1,23 +1,7 @@ -# -# Copyright 2023 Google LLC -# -# 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 -# -# https://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. -# - -POM_ARTIFACT_ID=navigationplugin -GROUP=com.rudderstack.android.navigationplugin +POM_ARTIFACT_ID=navigation +GROUP=com.rudderstack.android POM_PACKAGING=aar VERSION_CODE=1 -VERSION_NAME=0.1.0 -POM_NAME=Rudderstack plugin for listening to navigation events -POM_DESCRIPTION= A plugin for listening to navigation events +VERSION_NAME=1.0.0 +POM_NAME=Rudderstack navigation SDK for Android +POM_DESCRIPTION=Rudderstack navigation SDK for Android \ No newline at end of file diff --git a/libs/navigationplugin/package-lock.json b/libs/navigationplugin/package-lock.json new file mode 100644 index 000000000..558cd88d0 --- /dev/null +++ b/libs/navigationplugin/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "navigation", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "navigation", + "version": "1.0.0", + "license": "ISC" + } + } +} diff --git a/libs/navigationplugin/package.json b/libs/navigationplugin/package.json new file mode 100644 index 000000000..ef6b9d154 --- /dev/null +++ b/libs/navigationplugin/package.json @@ -0,0 +1,11 @@ +{ + "name": "navigation", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/libs/navigationplugin/project.json b/libs/navigationplugin/project.json new file mode 100644 index 000000000..b65db7c30 --- /dev/null +++ b/libs/navigationplugin/project.json @@ -0,0 +1,81 @@ +{ + "name": "navigation", + "$schema": "../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "./libs/navigationplugin/src", + "targets": { + "build": { + "executor": "@jnxplus/nx-gradle:build", + "dependsOn": [ + { + "projects": [ + "android", + "rudderreporter" + ], + "target": "build" + } + ], + "outputs": [ + "{projectRoot}/libs/navigationplugin/build" + ] + }, + "lint": { + "dependsOn": [ + "build" + ], + "executor": "@jnxplus/nx-gradle:lint", + "options": { + "linter": "ktlint" + } + }, + "test": { + "dependsOn": [ + "build" + ], + "executor": "@jnxplus/nx-gradle:test" + }, + "ktformat": { + "executor": "@jnxplus/nx-gradle:ktformat" + }, + "version": { + "executor": "@jscutlery/semver:version", + "options": { + "baseBranch": "master", + "preset": "conventional", + "tagPrefix": "${projectName}@" + } + }, + "sync-bumped-version-properties": { + "executor": "nx:run-commands", + "options": { + "command": "node gradle-updater.js 'libs/navigationplugin'" + } + }, + "github": { + "executor": "@jscutlery/semver:github", + "options": { + "tag": "navigation@1.0.0", + "notesFile": ".libs/navigationplugin/CHANGELOG_LATEST.md" + } + }, + "release-sonatype": { + "dependsOn": [ + "build" + ], + "executor": "nx:run-commands", + "options": { + "command": "echo 'navigation-release' && ./gradlew :libs:navigationplugin:publishToSonatype -Prelease closeAndReleaseSonatypeStagingRepository" + } + }, + "snapshot-release": { + "dependsOn": [ + "build" + ], + "executor": "nx:run-commands", + "options": { + "command": "echo 'navigation-snapshot' && ./gradlew :libs:navigationplugin:publishToSonatype" + } + } + }, + "tags": [] +} diff --git a/libs/sync/.gitignore b/libs/sync/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/libs/sync/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/libs/sync/build.gradle.kts b/libs/sync/build.gradle.kts new file mode 100644 index 000000000..3a6695cd7 --- /dev/null +++ b/libs/sync/build.gradle.kts @@ -0,0 +1,95 @@ +/* + * Creator: Debanjan Chatterjee on 18/03/24, 11:44 am Last modified: 18/03/24, 11:44 am + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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("org.jetbrains.kotlin.android") +} +val dependencyPath = "${project.projectDir.parentFile.parent}/dependencies.gradle" +apply(from = dependencyPath) +val deps: HashMap by extra +val library: HashMap by extra +val projects: HashMap by extra + +android { + compileSdk = library["target_sdk"].toString().toInt() + + defaultConfig { + minSdk = library["min_sdk"].toString().toInt() + consumerProguardFiles("consumer-rules.pro") + + //for code access + buildConfigField("String", "LIBRARY_VERSION_NAME", library["version_name"] as String) + } + buildFeatures { + buildConfig = true + } + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + javaParameters = true + } + testOptions { + unitTests { + this.isIncludeAndroidResources = true + } + } + namespace = "com.rudderstack.android.sync" +} + +dependencies { + implementation(deps["kotlinCore"].toString()) + compileOnly(project(":core")) + compileOnly(project(":android")) + //dependency on work manager + implementation(deps["work"].toString()) + implementation(deps["workMultiprocess"].toString()) + + testImplementation(project(":core")) + testImplementation(project(":android")) + testImplementation(deps["workTest"].toString()) + testImplementation(deps["androidXTest"].toString()) + testImplementation(deps["hamcrest"].toString()) + testImplementation(deps["mockito"].toString()) + testImplementation(deps["mockito_kotlin"].toString()) + testImplementation(deps["mockk"].toString()) + testImplementation(deps["mockk_agent_jvm"].toString()) + testImplementation(deps["awaitility"].toString()) + testImplementation(deps["robolectric"].toString()) + testImplementation(deps["androidXTestExtJunitKtx"].toString()) + testImplementation(deps["androidXTestRules"].toString()) + testImplementation(deps["junit"].toString()) + + + androidTestImplementation(deps["androidXTestExtJunitKtx"].toString()) +} +tasks.withType(type = org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs::class) { + kotlinOptions.jvmTarget = "1.8" +} + +apply(from = "${project.projectDir.parentFile.parent}/gradle/artifacts-aar.gradle") +apply(from = "${project.projectDir.parentFile.parent}/gradle/mvn-publish.gradle") +apply(from = "${project.projectDir.parentFile.parent}/gradle/codecov.gradle") diff --git a/libs/sync/config.properties b/libs/sync/config.properties new file mode 100644 index 000000000..554c2420d --- /dev/null +++ b/libs/sync/config.properties @@ -0,0 +1 @@ +platform="java-kotlin" diff --git a/libs/sync/consumer-rules.pro b/libs/sync/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/libs/sync/gradle.properties b/libs/sync/gradle.properties new file mode 100644 index 000000000..eefed9296 --- /dev/null +++ b/libs/sync/gradle.properties @@ -0,0 +1,7 @@ +POM_ARTIFACT_ID=sync +GROUP=com.rudderstack.android +POM_PACKAGING=aar +VERSION_CODE=1 +VERSION_NAME=1.0.0 +POM_NAME=Rudderstack Synchronization SDK for Android +POM_DESCRIPTION=Rudderstack Synchronization SDK for Android using Work Manager \ No newline at end of file diff --git a/libs/sync/package-lock.json b/libs/sync/package-lock.json new file mode 100644 index 000000000..11441e5b5 --- /dev/null +++ b/libs/sync/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "sync", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sync", + "version": "1.0.0", + "license": "ISC" + } + } +} diff --git a/libs/sync/package.json b/libs/sync/package.json new file mode 100644 index 000000000..7a003486e --- /dev/null +++ b/libs/sync/package.json @@ -0,0 +1,11 @@ +{ + "name": "sync", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/libs/sync/proguard-rules.pro b/libs/sync/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/libs/sync/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libs/sync/project.json b/libs/sync/project.json new file mode 100644 index 000000000..9a5b1d2cf --- /dev/null +++ b/libs/sync/project.json @@ -0,0 +1,81 @@ +{ + "name": "sync", + "$schema": "../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "./libs/sync/src", + "targets": { + "build": { + "executor": "@jnxplus/nx-gradle:build", + "dependsOn": [ + { + "projects": [ + "android", + "rudderreporter" + ], + "target": "build" + } + ], + "outputs": [ + "{projectRoot}/libs/sync/build" + ] + }, + "lint": { + "dependsOn": [ + "build" + ], + "executor": "@jnxplus/nx-gradle:lint", + "options": { + "linter": "ktlint" + } + }, + "test": { + "dependsOn": [ + "build" + ], + "executor": "@jnxplus/nx-gradle:test" + }, + "ktformat": { + "executor": "@jnxplus/nx-gradle:ktformat" + }, + "version": { + "executor": "@jscutlery/semver:version", + "options": { + "baseBranch": "master", + "preset": "conventional", + "tagPrefix": "${projectName}@" + } + }, + "sync-bumped-version-properties": { + "executor": "nx:run-commands", + "options": { + "command": "node gradle-updater.js 'libs/sync'" + } + }, + "github": { + "executor": "@jscutlery/semver:github", + "options": { + "tag": "navigation@1.0.0", + "notesFile": ".libs/sync/CHANGELOG_LATEST.md" + } + }, + "release-sonatype": { + "dependsOn": [ + "build" + ], + "executor": "nx:run-commands", + "options": { + "command": "echo 'sync-release' && ./gradlew :libs:sync:publishToSonatype -Prelease closeAndReleaseSonatypeStagingRepository" + } + }, + "snapshot-release": { + "dependsOn": [ + "build" + ], + "executor": "nx:run-commands", + "options": { + "command": "echo 'sync-snapshot' && ./gradlew :libs:sync:publishToSonatype" + } + } + }, + "tags": [] +} diff --git a/libs/sync/src/androidTest/java/com/rudderstack/android/sync/ExampleInstrumentedTest.kt b/libs/sync/src/androidTest/java/com/rudderstack/android/sync/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..1f869ec7a --- /dev/null +++ b/libs/sync/src/androidTest/java/com/rudderstack/android/sync/ExampleInstrumentedTest.kt @@ -0,0 +1,38 @@ +/* + * Creator: Debanjan Chatterjee on 18/03/24, 11:44 am Last modified: 18/03/24, 11:44 am + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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.rudderstack.android.sync + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.rudderstack.android.sync.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/libs/sync/src/main/AndroidManifest.xml b/libs/sync/src/main/AndroidManifest.xml new file mode 100644 index 000000000..7493ea647 --- /dev/null +++ b/libs/sync/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkManagerAnalyticsFactory.kt b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkManagerAnalyticsFactory.kt similarity index 92% rename from android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkManagerAnalyticsFactory.kt rename to libs/sync/src/main/java/com/rudderstack/android/sync/WorkManagerAnalyticsFactory.kt index 0ba844f2b..b163635bd 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkManagerAnalyticsFactory.kt +++ b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkManagerAnalyticsFactory.kt @@ -12,8 +12,8 @@ * permissions and limitations under the License. */ -package com.rudderstack.android.internal.infrastructure.sync - +package com.rudderstack.android.sync +import com.rudderstack.android.sync.internal.RudderSyncWorker import android.app.Application import com.rudderstack.core.Analytics diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkerManagerPlugin.kt b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt similarity index 66% rename from android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkerManagerPlugin.kt rename to libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt index 05386740c..e8822c039 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/sync/WorkerManagerPlugin.kt +++ b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt @@ -12,19 +12,26 @@ * permissions and limitations under the License. */ -package com.rudderstack.android.internal.infrastructure.sync +package com.rudderstack.android.sync import android.app.Application import com.rudderstack.android.currentConfigurationAndroid +import com.rudderstack.android.sync.internal.registerWorkManager +import com.rudderstack.android.sync.internal.unregisterWorkManager import com.rudderstack.core.Analytics import com.rudderstack.core.Configuration import com.rudderstack.core.InfrastructurePlugin abstract class WorkerManagerPlugin : InfrastructurePlugin { private var application: Application?= null + private var analyticsIdentifier: String? = null override fun setup(analytics: Analytics) { + analyticsIdentifier = analytics.writeKey val currentConfig = analytics.currentConfigurationAndroid if (currentConfig?.isPeriodicFlushEnabled != true) { + currentConfig?.logger?.error( + log = "Halting Worker manager plugin initialization since isPeriodicFlushEnabled configuration is false" + ) return } currentConfig.apply { @@ -35,13 +42,16 @@ abstract class WorkerManagerPlugin : InfrastructurePlugin { } } override fun shutdown() { - application?.unregisterWorkManager() + application?.unregisterWorkManager(analyticsIdentifier ?: return) + analyticsIdentifier = null } + /** + * Internal classes are not supported. + * This is because instantiating an inner class requires the parent class instance. + * It's not worth it to try finding an instance in Heap. Cause this approach might conflict + * with garbage collector + */ abstract val workManagerAnalyticsFactoryClassName: Class - override fun updateConfiguration(configuration: Configuration) { - // no -op - } - } \ No newline at end of file diff --git a/libs/sync/src/main/java/com/rudderstack/android/sync/internal/DataSyncWorkManagerExtensions.kt b/libs/sync/src/main/java/com/rudderstack/android/sync/internal/DataSyncWorkManagerExtensions.kt new file mode 100644 index 000000000..5b730621e --- /dev/null +++ b/libs/sync/src/main/java/com/rudderstack/android/sync/internal/DataSyncWorkManagerExtensions.kt @@ -0,0 +1,117 @@ +/* + * Creator: Debanjan Chatterjee on 18/03/24, 6:22 pm Last modified: 04/01/24, 5:47 pm + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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.rudderstack.android.sync.internal + +import android.app.Application +import androidx.annotation.VisibleForTesting +import androidx.work.Configuration +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.multiprocess.RemoteWorkManager +import com.rudderstack.android.currentConfigurationAndroid +import com.rudderstack.android.sync.WorkManagerAnalyticsFactory +import com.rudderstack.core.Analytics +import java.lang.ref.WeakReference +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit + +/** + * Aids in data syncing to server with help of work manager. + * Only works if work manager dependency is added externally + * Also, if multi-process support is needed, set the default process name. + * For multi-process support, the following dependency is expected + * Implementation "androidx.work:work-multiprocess:2.5.x" + */ +private const val WORK_MANAGER_TAG = "rudder_sync" +private const val WORK_NAME = "rudder_sync_work" +private const val REPEAT_INTERVAL_IN_MINS = 15L +private var analyticsRefMap = ConcurrentHashMap>() + +private fun Analytics.generateKeyForLabel(label: String) = addKeyToLabel(label, writeKey) +private fun addKeyToLabel(label: String, key: String) = "${label}_$key" +internal fun getAnalytics(key: String) = analyticsRefMap[key]?.get() + + +private val constraints by lazy { + Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() +} + +private fun Analytics.syncWorker( + workManagerAnalyticsFactoryClassName: Class +) = PeriodicWorkRequestBuilder( + REPEAT_INTERVAL_IN_MINS, TimeUnit.MINUTES) + .setInitialDelay(REPEAT_INTERVAL_IN_MINS, TimeUnit.MINUTES) + .setConstraints(constraints) + .setInputData( + workerInputData(workManagerAnalyticsFactoryClassName)) + .addTag(generateKeyForLabel(WORK_MANAGER_TAG)).build() + +@VisibleForTesting +internal fun Analytics.workerInputData(workManagerAnalyticsFactoryClassName: Class) = + Data.Builder() + .putString( + WORKER_ANALYTICS_FACTORY_KEY, workManagerAnalyticsFactoryClassName.name) + .putString(WORKER_ANALYTICS_INSTANCE_KEY, writeKey) + .build() + +internal fun Application.registerWorkManager( + analytics: Analytics, workManagerAnalyticsFactoryClass: Class +) { + analytics.logger.debug(log = "Initializing work manager") + if (getAnalytics(analytics.writeKey)?.takeUnless { it.isShutdown } != null) { + analytics.logger.debug(log = "Work manager already initialized") + return + } + + analyticsRefMap[analytics.writeKey] = WeakReference(analytics) + + Configuration.Builder().also { + // if process name is available, this is a multi-process app + analytics.currentConfigurationAndroid?.defaultProcessName?.apply { + it.setDefaultProcessName(this) + } + analytics.currentConfigurationAndroid?.networkExecutor?.apply { + it.setExecutor(this) + } + } + if (analytics.currentConfigurationAndroid?.multiProcessEnabled == true) { + RemoteWorkManager.getInstance(this).enqueueUniquePeriodicWork( + analytics.generateKeyForLabel(WORK_NAME), + ExistingPeriodicWorkPolicy.KEEP, + analytics.syncWorker(workManagerAnalyticsFactoryClass) + ) + } else { + WorkManager.getInstance(this).enqueueUniquePeriodicWork( + analytics.generateKeyForLabel(WORK_NAME), + ExistingPeriodicWorkPolicy.KEEP, + analytics.syncWorker(workManagerAnalyticsFactoryClass) + ) + } + +} + +fun Application.unregisterWorkManager(writeKey: String) { + analyticsRefMap[writeKey]?.clear() + analyticsRefMap.remove(writeKey) + WorkManager.getInstance(this) + .cancelAllWorkByTag(addKeyToLabel(WORK_MANAGER_TAG, writeKey)) + RemoteWorkManager.getInstance(this) + .cancelAllWorkByTag(addKeyToLabel(WORK_MANAGER_TAG, writeKey)) +} diff --git a/libs/sync/src/main/java/com/rudderstack/android/sync/internal/RudderSyncWorker.kt b/libs/sync/src/main/java/com/rudderstack/android/sync/internal/RudderSyncWorker.kt new file mode 100644 index 000000000..856c7c075 --- /dev/null +++ b/libs/sync/src/main/java/com/rudderstack/android/sync/internal/RudderSyncWorker.kt @@ -0,0 +1,65 @@ +/* + * Creator: Debanjan Chatterjee on 18/03/24, 6:20 pm Last modified: 18/03/24, 6:11 pm + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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.rudderstack.android.sync.internal + +import android.app.Application +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.rudderstack.android.sync.WorkManagerAnalyticsFactory +import com.rudderstack.core.Analytics + + +internal const val WORKER_ANALYTICS_FACTORY_KEY = "WORKER_ANALYTICS_FACTORY_KEY" +internal const val WORKER_ANALYTICS_INSTANCE_KEY = "WORKER_ANALYTICS_INSTANCE_KEY" +/** + * Syncs the data at an interval with rudder server + * + */ +internal class RudderSyncWorker( + appContext: Context, workerParams: WorkerParameters +) : Worker(appContext, workerParams) { + override fun doWork(): Result { + (applicationContext as? Application)?.let { + val analyticsInstanceKey = + inputData.getString(WORKER_ANALYTICS_INSTANCE_KEY) ?: return Result.failure() + val weakSyncAnalytics = getAnalytics(analyticsInstanceKey) + if (weakSyncAnalytics?.isShutdown == true) { + weakSyncAnalytics.logger.warn(log = "Cannot do work. Analytics instance is " + + "already shutdown") + return Result.failure() + } + val syncAnalytics = (weakSyncAnalytics ?: createSyncAnalytics()) + val success = syncAnalytics?.blockingFlush() + syncAnalytics?.logger?.debug(log = "Data upload through worker. success: $success") + if (weakSyncAnalytics == null) { + syncAnalytics?.shutdown() + } + + return if (success == true) Result.success() else Result.failure() + } + return Result.failure() + } + + private fun createSyncAnalytics(): Analytics? { + val analyticsFactoryClassName = inputData.getString(WORKER_ANALYTICS_FACTORY_KEY) + return analyticsFactoryClassName?.let { + val analyticsFactory = Class.forName(it).getDeclaredConstructor().newInstance() as WorkManagerAnalyticsFactory + analyticsFactory.createAnalytics(applicationContext as Application) + } + + } + +} \ No newline at end of file diff --git a/android/src/test/java/com/rudderstack/android/RudderSyncWorkerTest.kt b/libs/sync/src/test/java/com/rudderstack/android/sync/RudderSyncWorkerTest.kt similarity index 67% rename from android/src/test/java/com/rudderstack/android/RudderSyncWorkerTest.kt rename to libs/sync/src/test/java/com/rudderstack/android/sync/RudderSyncWorkerTest.kt index d64622b2a..9c8f74e4d 100644 --- a/android/src/test/java/com/rudderstack/android/RudderSyncWorkerTest.kt +++ b/libs/sync/src/test/java/com/rudderstack/android/sync/RudderSyncWorkerTest.kt @@ -1,6 +1,6 @@ /* - * Creator: Debanjan Chatterjee on 07/09/22, 12:19 PM Last modified: 07/09/22, 12:08 PM - * Copyright: All rights reserved Ⓒ 2022 http://rudderstack.com + * Creator: Debanjan Chatterjee on 18/03/24, 12:47 pm Last modified: 18/03/24, 12:13 pm + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com * * 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 @@ -12,28 +12,33 @@ * permissions and limitations under the License. */ -package com.rudderstack.android +package com.rudderstack.android.sync import android.app.Application import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.work.ListenableWorker import androidx.work.testing.TestWorkerBuilder -import com.rudderstack.android.utils.TestLogger -import com.rudderstack.android.internal.infrastructure.sync.RudderSyncWorker -import com.rudderstack.android.internal.infrastructure.sync.WorkManagerAnalyticsFactory -import com.rudderstack.android.internal.infrastructure.sync.registerWorkManager +import com.rudderstack.android.ConfigurationAndroid +import com.rudderstack.android.currentConfigurationAndroid +import com.rudderstack.android.sync.internal.RudderSyncWorker +import com.rudderstack.android.sync.internal.workerInputData +import com.rudderstack.android.sync.utils.TestLogger import com.rudderstack.core.Analytics import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.junit4.MockKRule +import io.mockk.just import io.mockk.mockk +import io.mockk.runs import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith - import org.robolectric.annotation.Config import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -53,7 +58,6 @@ class RudderSyncWorkerTest { @Before fun setup(){ MockKAnnotations.init() -// MockitoAnnotations.openMocks(this) application = ApplicationProvider.getApplicationContext() val logger = TestLogger() every{configuration.defaultProcessName} returns null @@ -62,22 +66,30 @@ class RudderSyncWorkerTest { every{analytics.logger} returns logger every{analytics.currentConfigurationAndroid} returns configuration every{analytics.currentConfiguration} returns configuration - application.registerWorkManager(analytics, DummyAnalyticsFactory::class.java) + every { analytics.writeKey } returns "test_write_key" + every { analytics.shutdown() } just runs executorService = Executors.newSingleThreadExecutor() - + DummyAnalyticsFactory.analytics = analytics } @Test fun testRudderSyncWorker(){ every{analytics.blockingFlush()} returns true every { analytics.isShutdown } returns false - val worker = TestWorkerBuilder.from(application, RudderSyncWorker::class.java, executorService).build() + val worker = TestWorkerBuilder(application,executorService, + ).setInputData(analytics.workerInputData( + DummyAnalyticsFactory::class.java + )).build() val result = worker.doWork() + assertThat(result, Matchers.`is`(ListenableWorker.Result.success())) verify(exactly = 1){ analytics.blockingFlush() } } - inner class DummyAnalyticsFactory: WorkManagerAnalyticsFactory { + class DummyAnalyticsFactory: WorkManagerAnalyticsFactory { + companion object{ + lateinit var analytics: Analytics + } override fun createAnalytics(application: Application): Analytics { return analytics } diff --git a/android/src/test/java/com/rudderstack/android/utils/TestLogger.kt b/libs/sync/src/test/java/com/rudderstack/android/sync/utils/TestLogger.kt similarity index 96% rename from android/src/test/java/com/rudderstack/android/utils/TestLogger.kt rename to libs/sync/src/test/java/com/rudderstack/android/sync/utils/TestLogger.kt index f1c61664c..f75d17ee7 100644 --- a/android/src/test/java/com/rudderstack/android/utils/TestLogger.kt +++ b/libs/sync/src/test/java/com/rudderstack/android/sync/utils/TestLogger.kt @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.rudderstack.android.utils +package com.rudderstack.android.sync.utils import com.rudderstack.core.Logger diff --git a/models/src/main/java/com/rudderstack/models/Message.kt b/models/src/main/java/com/rudderstack/models/Message.kt index 4bd13835f..35c924297 100644 --- a/models/src/main/java/com/rudderstack/models/Message.kt +++ b/models/src/main/java/com/rudderstack/models/Message.kt @@ -77,7 +77,12 @@ sealed class Message( @SerializedName("messageId") @JsonProperty("messageId") @Json(name = "messageId") - val messageId: String = _messageId ?: UUID.randomUUID().toString() + val messageId: String = _messageId ?: String.format( + Locale.US, + "%d-%s", + System.currentTimeMillis(), + UUID.randomUUID().toString(), + ) // ugly hack for moshi // https://github.com/square/moshi/issues/609#issuecomment-798805367 @@ -666,7 +671,7 @@ class TrackMessage internal constructor( destinationProps, eventName, properties, -// _messageId = this.messageId, + _messageId = this.messageId, ) override fun toString(): String { diff --git a/moshirudderadapter/build.gradle b/moshirudderadapter/build.gradle index e5514a997..c6275f30c 100644 --- a/moshirudderadapter/build.gradle +++ b/moshirudderadapter/build.gradle @@ -35,7 +35,7 @@ apply from : "$projectDir/../dependencies.gradle" dependencies { - implementation(project(path: ":rudderjsonadapter", configuration: 'default')) + implementation(project(path: ":rudderjsonadapter")) api deps.moshi.kotlin api deps.moshi.core //test diff --git a/samples/sample-kotlin-android/build.gradle.kts b/samples/sample-kotlin-android/build.gradle.kts index ea5f16c29..c858b9b2e 100644 --- a/samples/sample-kotlin-android/build.gradle.kts +++ b/samples/sample-kotlin-android/build.gradle.kts @@ -90,6 +90,7 @@ android { } kotlinOptions { jvmTarget = "17" + javaParameters = true } java { toolchain { @@ -110,11 +111,6 @@ android { buildFeatures { buildConfig = true } - packagingOptions { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - } - } } dependencies { @@ -149,8 +145,9 @@ dependencies { implementation(project(":core")) implementation(project(":models")) implementation(project(":web")) - implementation(project(":rudderjsonadapter")) implementation(project(":rudderreporter")) + implementation(project(":libs:sync")) + implementation(project(":libs:navigationplugin")) testImplementation("junit:junit:4.13.2") diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/RudderAnalyticsUtils.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/RudderAnalyticsUtils.kt index 4e449c318..6b6da81ee 100644 --- a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/RudderAnalyticsUtils.kt +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/RudderAnalyticsUtils.kt @@ -8,24 +8,58 @@ import com.rudderstack.android.ruddermetricsreporterandroid.Configuration import com.rudderstack.android.ruddermetricsreporterandroid.DefaultRudderReporter import com.rudderstack.android.ruddermetricsreporterandroid.LibraryMetadata import com.rudderstack.android.ruddermetricsreporterandroid.RudderReporter +import com.rudderstack.android.sampleapp.analytics.workmanger.SampleWorkManagerPlugin import com.rudderstack.core.Analytics import com.rudderstack.gsonrudderadapter.GsonAdapter import com.rudderstack.jacksonrudderadapter.JacksonAdapter object RudderAnalyticsUtils { - private const val PRIMARY_INSTANCE_NAME = "primary" - private const val SECONDARY_INSTANCE_NAME = "secondary" private var _rudderAnalytics: Analytics? = null private var _rudderAnalyticsSecondary: Analytics? = null private var _rudderReporter: RudderReporter? = null fun initialize(application: Application, listener: InitializationListener? = null) { - _rudderAnalytics = createInstance( + //wen add work manager support to this instance + _rudderAnalytics = createPrimaryAnalyticsInstanceWithWorkerSupport(application, listener) + _rudderAnalyticsSecondary = createSecondaryInstance(listener, application) + _rudderReporter = DefaultRudderReporter( + context = application, baseUrl = METRICS_BASE_URL, configuration = Configuration( + LibraryMetadata( + name = "android", + sdkVersion = BuildConfig.LIBRARY_PACKAGE_NAME, + versionCode = BuildConfig.LIBRARY_VERSION_NAME, + writeKey = WRITE_KEY + ) + ), JacksonAdapter() + ) + _rudderAnalytics?.initializeWorkManager() + } + + private fun createSecondaryInstance( + listener: InitializationListener?, + application: Application + ) = createInstance( + writeKey = WRITE_KEY_SECONDARY, + initializationListener = { success, message -> + listener?.onAnalyticsInitialized(WRITE_KEY_SECONDARY, success, message) + }, + configuration = ConfigurationAndroid( + application = application, + GsonAdapter(), + dataPlaneUrl = DATA_PLANE_URL_SECONDARY, + controlPlaneUrl = CONTROL_PLANE_URL_SECONDARY, + trackLifecycleEvents = true, + recordScreenViews = true, + ) + ) + + fun createPrimaryAnalyticsInstanceWithWorkerSupport(application: Application, listener: InitializationListener? = null): Analytics { + return createInstance( writeKey = WRITE_KEY, initializationListener = { success, message -> - listener?.onAnalyticsInitialized(PRIMARY_INSTANCE_NAME, success, message) + listener?.onAnalyticsInitialized(WRITE_KEY, success, message) }, configuration = ConfigurationAndroid( application = application, @@ -34,32 +68,13 @@ object RudderAnalyticsUtils { controlPlaneUrl = CONTROL_PLANE_URL, trackLifecycleEvents = true, recordScreenViews = true, + isPeriodicFlushEnabled = true + ) ) - _rudderAnalyticsSecondary = createInstance( - writeKey = WRITE_KEY_SECONDARY, - initializationListener = { success, message -> - listener?.onAnalyticsInitialized(SECONDARY_INSTANCE_NAME, success, message) - }, - configuration = ConfigurationAndroid( - application = application, - GsonAdapter(), - dataPlaneUrl = DATA_PLANE_URL_SECONDARY, - controlPlaneUrl = CONTROL_PLANE_URL_SECONDARY, - trackLifecycleEvents = true, - recordScreenViews = true, - ) - ) - _rudderReporter = DefaultRudderReporter( - context = application, baseUrl = METRICS_BASE_URL, configuration = Configuration( - LibraryMetadata( - name = "android", - sdkVersion = BuildConfig.LIBRARY_PACKAGE_NAME, - versionCode = BuildConfig.LIBRARY_VERSION_NAME, - writeKey = WRITE_KEY - ) - ), JacksonAdapter() - ) + } + private fun Analytics.initializeWorkManager() { + addInfrastructurePlugin(SampleWorkManagerPlugin()) } val primaryAnalytics: Analytics @@ -79,7 +94,7 @@ object RudderAnalyticsUtils { fun interface InitializationListener { - fun onAnalyticsInitialized(instanceName: String, success: Boolean, message: String?) + fun onAnalyticsInitialized(writeKey: String, success: Boolean, message: String?) } } diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerAnalyticsFactory.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerAnalyticsFactory.kt new file mode 100644 index 000000000..a19e1f07a --- /dev/null +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerAnalyticsFactory.kt @@ -0,0 +1,26 @@ +/* + * Creator: Debanjan Chatterjee on 22/03/24, 1:06 pm Last modified: 22/03/24, 1:06 pm + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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.rudderstack.android.sampleapp.analytics.workmanger + +import android.app.Application +import com.rudderstack.android.sampleapp.analytics.RudderAnalyticsUtils +import com.rudderstack.android.sync.WorkManagerAnalyticsFactory +import com.rudderstack.core.Analytics + +class SampleWorkManagerAnalyticsFactory : WorkManagerAnalyticsFactory { + override fun createAnalytics(application: Application): Analytics { + return RudderAnalyticsUtils.createPrimaryAnalyticsInstanceWithWorkerSupport(application) + } +} \ No newline at end of file diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt new file mode 100644 index 000000000..263a1b573 --- /dev/null +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt @@ -0,0 +1,23 @@ +/* + * Creator: Debanjan Chatterjee on 22/03/24, 12:45 pm Last modified: 22/03/24, 12:45 pm + * Copyright: All rights reserved Ⓒ 2024 http://rudderstack.com + * + * 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.rudderstack.android.sampleapp.analytics.workmanger + +import com.rudderstack.android.sync.WorkManagerAnalyticsFactory +import com.rudderstack.android.sync.WorkerManagerPlugin + +class SampleWorkManagerPlugin: WorkerManagerPlugin() { + override val workManagerAnalyticsFactoryClassName: Class + get() = SampleWorkManagerAnalyticsFactory::class.java +} \ No newline at end of file diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt index 26ed23896..8288814c8 100644 --- a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt @@ -8,6 +8,7 @@ import com.rudderstack.android.sampleapp.analytics.RudderAnalyticsUtils.primaryA import com.rudderstack.android.sampleapp.analytics.RudderAnalyticsUtils.secondaryAnalytics import com.rudderstack.android.utilities.endSession import com.rudderstack.android.utilities.startSession +import com.rudderstack.core.Analytics import com.rudderstack.core.Plugin import com.rudderstack.core.RudderOptions import com.rudderstack.models.GroupTraits @@ -29,7 +30,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { private val _loggingInterceptor by lazy { object : Plugin { - private var logsName: String = "Sample app" + private var _writeKey: String? = null + override fun setup(analytics: Analytics) { + _writeKey = analytics.writeKey + } + private var logsName: String = "Sample app-$_writeKey" override fun intercept(chain: Plugin.Chain): Message { val msg = chain.message() diff --git a/settings.gradle b/settings.gradle index bed1b619c..9df859678 100644 --- a/settings.gradle +++ b/settings.gradle @@ -41,4 +41,5 @@ include ':moshirudderadapter' include ':libs:navigationplugin' //include ':libs:testcommon' include ':libs:test-common' -include ':samples:sample-kotlin-android' \ No newline at end of file +include ':libs:sync' +include ':samples:sample-kotlin-android' diff --git a/web/build.gradle b/web/build.gradle index b8bb56eec..96258e76c 100644 --- a/web/build.gradle +++ b/web/build.gradle @@ -35,7 +35,7 @@ compileTestKotlin { dependencies { - implementation(project(path: ":rudderjsonadapter", configuration: 'default')) + implementation(project(path: ":rudderjsonadapter")) //test testImplementation 'junit:junit:4.13.2'