diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a9a7f6efc7..1ed03b3f01 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -77,7 +77,6 @@ androidx-test-junit = "androidx.test.ext:junit:1.1.5" androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" } androidx-work = { module = "androidx.work:work-runtime", version.ref = "androidx-work" } androidx-work-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } -appsflyer = "com.appsflyer:af-android-sdk:6.12.4" circuit-codegen-annotations = { module = "com.slack.circuit:circuit-codegen-annotations", version.ref = "circuit" } circuit-foundation = { module = "com.slack.circuit:circuit-foundation", version.ref = "circuit" } circuit-test = { module = "com.slack.circuit:circuit-test", version.ref = "circuit" } diff --git a/library/analytics/build.gradle.kts b/library/analytics/build.gradle.kts index 20fb1996ff..2310c19179 100644 --- a/library/analytics/build.gradle.kts +++ b/library/analytics/build.gradle.kts @@ -10,10 +10,6 @@ android { configureCompose(project) createEventBusIndex("org.cru.godtools.analytics.AnalyticsEventBusIndex") - - defaultConfig { - buildConfigField("String", "APPSFLYER_DEV_KEY", "\"QdbVaVHi9bHRchUTWtoaij\"") - } } dependencies { @@ -31,7 +27,6 @@ dependencies { implementation(libs.gtoSupport.compat) implementation(libs.gtoSupport.dagger) - implementation(libs.appsflyer) implementation(libs.dagger) implementation(libs.eventbus) implementation(libs.facebook) diff --git a/library/analytics/src/debug/AndroidManifest.xml b/library/analytics/src/debug/AndroidManifest.xml deleted file mode 100644 index 494bab05af..0000000000 --- a/library/analytics/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/library/analytics/src/main/AndroidManifest.xml b/library/analytics/src/main/AndroidManifest.xml index 871daddf39..60acdf49af 100644 --- a/library/analytics/src/main/AndroidManifest.xml +++ b/library/analytics/src/main/AndroidManifest.xml @@ -1,36 +1,13 @@ - - - - - - - - - + + + - - - - - - - - - - - - - diff --git a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsService.kt b/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsService.kt deleted file mode 100644 index 4ca7a4c344..0000000000 --- a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsService.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.cru.godtools.analytics.appsflyer - -import android.app.Activity -import android.app.Application -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import androidx.annotation.VisibleForTesting -import com.appsflyer.AFLogger -import com.appsflyer.AppsFlyerConversionListener -import com.appsflyer.AppsFlyerLib -import com.appsflyer.deeplink.DeepLink -import com.appsflyer.deeplink.DeepLinkListener -import com.appsflyer.deeplink.DeepLinkResult -import com.karumi.weak.weak -import javax.inject.Inject -import javax.inject.Singleton -import org.cru.godtools.analytics.BuildConfig -import org.cru.godtools.base.HOST_GET_GODTOOLSAPP_COM -import timber.log.Timber - -private const val TAG = "AppsFlyerAnalytics" - -@VisibleForTesting -internal const val AF_DP = "af_dp" -private const val AF_STATUS = "af_status" -private const val IS_FIRST_LAUNCH = "is_first_launch" - -private const val STATUS_NON_ORGANIC = "Non-organic" - -@Singleton -class AppsFlyerAnalyticsService @VisibleForTesting internal constructor( - private val app: Application, - private val deepLinkResolvers: Set, - appsFlyer: AppsFlyerLib -) : Application.ActivityLifecycleCallbacks { - @Inject - internal constructor( - app: Application, - deepLinkResolvers: Set<@JvmSuppressWildcards AppsFlyerDeepLinkResolver> - ) : this(app, deepLinkResolvers, AppsFlyerLib.getInstance()) - - // region Application.ActivityLifecycleCallbacks - private var activeActivity: Activity? by weak() - - override fun onActivityResumed(activity: Activity) { - activeActivity = activity - } - - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit - override fun onActivityStarted(activity: Activity) = Unit - override fun onActivityPaused(activity: Activity) = Unit - override fun onActivityStopped(activity: Activity) = Unit - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit - override fun onActivityDestroyed(activity: Activity) = Unit - // endregion Application.ActivityLifecycleCallbacks - - // region AppsFlyerConversionListener - @Deprecated("The legacy AppsFlyerConversionListener should be removed in favor of the DeepLinkListener") - @VisibleForTesting - internal val conversionListener = object : AppsFlyerConversionListener { - override fun onConversionDataSuccess(data: Map) { - Timber.tag(TAG).d("onConversionDataSuccess($data)") - if (data[IS_FIRST_LAUNCH] == true && data[AF_STATUS] == STATUS_NON_ORGANIC) { - onAppOpenAttribution(mapOf(AF_DP to data[AF_DP] as? String)) - } - } - - override fun onAppOpenAttribution(data: Map) { - Timber.tag(TAG).d("onAppOpenAttribution($data)") - val uri = data[AF_DP]?.let { Uri.parse(it).normalizeScheme() } - deepLinkResolvers.asSequence().mapNotNull { it.resolve(app, uri, data) }.firstOrNull()?.let { intent -> - activeActivity?.startActivity(intent) - } - } - - override fun onConversionDataFail(error: String) = Unit - override fun onAttributionFailure(error: String) = Unit - } - // endregion AppsFlyerConversionListener - - private val deepLinkListener = DeepLinkListener { result -> - if (result.status != DeepLinkResult.Status.FOUND) return@DeepLinkListener - val deepLink = result.deepLink ?: return@DeepLinkListener - val intent = deepLinkResolvers.mapNotNull { it.resolve(app, deepLink) }.firstOrNull() - if (intent != null) activeActivity?.startActivity(intent) - } - - init { - appsFlyer.apply { - if (BuildConfig.DEBUG) setLogLevel(AFLogger.LogLevel.DEBUG) - setOneLinkCustomDomain(HOST_GET_GODTOOLSAPP_COM) - subscribeForDeepLink(deepLinkListener) - init(BuildConfig.APPSFLYER_DEV_KEY, conversionListener, app) - start(app) - } - app.registerActivityLifecycleCallbacks(this) - } -} - -interface AppsFlyerDeepLinkResolver { - fun resolve(context: Context, deepLink: DeepLink): Intent? = deepLink.deepLinkValue?.let { resolve(context, it) } - fun resolve(context: Context, deepLinkValue: String): Intent? = null - - @Deprecated( - "This is the callback for the legacy AppsFlyerConversionListener, " + - "this should be replaced with logic to handle AppsFlyer DeepLinks" - ) - fun resolve(context: Context, uri: Uri?, data: Map): Intent? = null -} diff --git a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerModule.kt b/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerModule.kt deleted file mode 100644 index c61d71f0d2..0000000000 --- a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.cru.godtools.analytics.appsflyer - -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.IntoSet -import dagger.multibindings.Multibinds -import org.ccci.gto.android.common.dagger.eager.EagerSingleton - -@Module -@InstallIn(SingletonComponent::class) -abstract class AppsFlyerModule { - @Binds - @IntoSet - @EagerSingleton(threadMode = EagerSingleton.ThreadMode.MAIN) - abstract fun eagerSingleton(service: AppsFlyerAnalyticsService): Any - - @Multibinds - abstract fun deepLinkResolvers(): Set -} diff --git a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerSpringboardActivity.kt b/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerSpringboardActivity.kt deleted file mode 100644 index 327f593da1..0000000000 --- a/library/analytics/src/main/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerSpringboardActivity.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.cru.godtools.analytics.appsflyer - -import android.app.Activity -import android.os.Bundle -import org.cru.godtools.base.ui.startDashboardActivity - -class AppsFlyerSpringboardActivity : Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - startDashboardActivity() - finish() - } -} diff --git a/library/analytics/src/release/AndroidManifest.xml b/library/analytics/src/release/AndroidManifest.xml deleted file mode 100644 index df41e70297..0000000000 --- a/library/analytics/src/release/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/library/analytics/src/test/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsServiceTest.kt b/library/analytics/src/test/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsServiceTest.kt deleted file mode 100644 index 9d6cd05ecd..0000000000 --- a/library/analytics/src/test/kotlin/org/cru/godtools/analytics/appsflyer/AppsFlyerAnalyticsServiceTest.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.cru.godtools.analytics.appsflyer - -import android.app.Activity -import android.content.Intent -import android.net.Uri -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.mockk.confirmVerified -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.BeforeTest -import kotlin.test.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class AppsFlyerAnalyticsServiceTest { - private val activity = mockk(relaxUnitFun = true) - private lateinit var deepLinkResolver: AppsFlyerDeepLinkResolver - - private lateinit var analyticsService: AppsFlyerAnalyticsService - - @BeforeTest - fun setupMocks() { - deepLinkResolver = mockk { - every { resolve(any(), any(), any()) } returns null - } - - analyticsService = AppsFlyerAnalyticsService( - mockk(relaxUnitFun = true), - deepLinkResolvers = setOf(deepLinkResolver), - appsFlyer = mockk(relaxed = true) - ) - } - - // region conversionListener.onAppOpenAttribution() - @Test - fun verifyOnAppOpenAttributionCallsResolverWithUri() { - val uri = Uri.parse("https://example.com") - val data = mapOf(AF_DP to uri.toString()) - - analyticsService.conversionListener.onAppOpenAttribution(data) - verify { deepLinkResolver.resolve(any(), uri, data) } - } - - @Test - fun verifyOnAppOpenAttributionCallsResolverWithoutUrl() { - val data = emptyMap() - - analyticsService.conversionListener.onAppOpenAttribution(data) - verify { deepLinkResolver.resolve(any(), null, data) } - } - - @Test - fun verifyOnAppOpenAttributionOpensReturnedIntent() { - val intent = mockk() - analyticsService.onActivityResumed(activity) - every { deepLinkResolver.resolve(any(), any(), any()) } returns intent - - analyticsService.conversionListener.onAppOpenAttribution(emptyMap()) - verify { activity.startActivity(intent) } - confirmVerified(activity) - } - - @Test - fun verifyOnAppOpenAttributionDoesntOpenNullIntent() { - analyticsService.onActivityResumed(activity) - every { deepLinkResolver.resolve(any(), any(), any()) } returns null - - analyticsService.conversionListener.onAppOpenAttribution(emptyMap()) - verify(inverse = true) { activity.startActivity(any()) } - confirmVerified(activity) - } - // endregion conversionListener.onAppOpenAttribution() -}