1616
1717package com.duckduckgo.app.referral
1818
19- import android.content.ComponentName
20- import android.content.Context
21- import android.content.Intent
22- import android.content.pm.PackageManager
23- import com.android.installreferrer.api.InstallReferrerClient
24- import com.android.installreferrer.api.InstallReferrerClient.InstallReferrerResponse.*
25- import com.android.installreferrer.api.InstallReferrerStateListener
26- import com.duckduckgo.app.playstore.PlayStoreAndroidUtils.Companion.PLAY_STORE_PACKAGE
27- import com.duckduckgo.app.playstore.PlayStoreAndroidUtils.Companion.PLAY_STORE_REFERRAL_SERVICE
28- import com.duckduckgo.app.referral.AppInstallationReferrerStateListener.Companion.MAX_REFERRER_WAIT_TIME_MS
29- import com.duckduckgo.app.referral.ParseFailureReason.*
30- import com.duckduckgo.app.referral.ParsedReferrerResult.*
31- import com.duckduckgo.app.statistics.AtbInitializerListener
32- import com.duckduckgo.app.statistics.VariantManager
33- import kotlinx.coroutines.delay
34- import timber.log.Timber
35- import javax.inject.Inject
36- import javax.inject.Singleton
37-
3819interface AppInstallationReferrerStateListener {
3920
4021 fun initialiseReferralRetrieval ()
@@ -45,151 +26,3 @@ interface AppInstallationReferrerStateListener {
4526 }
4627
4728}
48-
49- @Singleton
50- class PlayStoreAppReferrerStateListener @Inject constructor(
51- val context : Context ,
52- private val packageManager : PackageManager ,
53- private val appInstallationReferrerParser : AppInstallationReferrerParser ,
54- private val appReferrerDataStore : AppReferrerDataStore ,
55- private val variantManager : VariantManager
56- ) : InstallReferrerStateListener, AppInstallationReferrerStateListener, AtbInitializerListener {
57-
58- private val referralClient = InstallReferrerClient .newBuilder(context).build()
59- private var initialisationStartTime: Long = 0
60-
61- private var referralResult: ParsedReferrerResult = ReferrerInitialising
62-
63- /* *
64- * Initialises the referrer service. This should only be called once.
65- */
66- override fun initialiseReferralRetrieval () {
67- try {
68- initialisationStartTime = System .currentTimeMillis()
69-
70- if (appReferrerDataStore.referrerCheckedPreviously) {
71-
72- referralResult = if (appReferrerDataStore.installedFromEuAuction) {
73- EuAuctionReferrerFound (fromCache = true )
74- } else {
75- loadPreviousReferrerData()
76- }
77-
78- Timber .i(" Already inspected this referrer data" )
79- return
80- }
81-
82- if (playStoreReferralServiceInstalled()) {
83- referralClient.startConnection(this )
84- } else {
85- referralResult = ParseFailure (ReferralServiceUnavailable )
86- }
87- } catch (e: RuntimeException ) {
88- Timber .w(e, " Failed to obtain referrer information" )
89- referralResult = ParseFailure (UnknownError )
90- }
91- }
92-
93- private fun loadPreviousReferrerData (): ParsedReferrerResult {
94- val suffix = loadFromDataStore()
95- return if (suffix == null ) {
96- Timber .i(" Already saw referrer data, but no campaign suffix saved" )
97- ReferrerNotFound (fromCache = true )
98- } else {
99- Timber .i(" Already have referrer data from previous run - $suffix " )
100- CampaignReferrerFound (suffix, fromCache = true )
101- }
102- }
103-
104- override fun onInstallReferrerSetupFinished (responseCode : Int ) {
105- val referrerRetrievalDurationMs = System .currentTimeMillis() - initialisationStartTime
106- Timber .i(" Took ${referrerRetrievalDurationMs} ms to get initial referral data callback" )
107- try {
108- when (responseCode) {
109- OK -> {
110- Timber .d(" Successfully connected to Referrer service" )
111- val response = referralClient.installReferrer
112- val referrer = response.installReferrer
113- val parsedResult = appInstallationReferrerParser.parse(referrer)
114- referralResultReceived(parsedResult)
115- }
116- FEATURE_NOT_SUPPORTED -> referralResultFailed(FeatureNotSupported )
117- SERVICE_UNAVAILABLE -> referralResultFailed(ServiceUnavailable )
118- DEVELOPER_ERROR -> referralResultFailed(DeveloperError )
119- SERVICE_DISCONNECTED -> referralResultFailed(ServiceDisconnected )
120- else -> referralResultFailed(UnknownError )
121-
122- }
123-
124- referralClient.endConnection()
125- } catch (e: RuntimeException ) {
126- Timber .w(e, " Failed to retrieve referrer data" )
127- referralResultFailed(UnknownError )
128- }
129- }
130-
131- /* *
132- * Retrieves the app installation referral code.
133- * This might return a result immediately or might wait for a result to become available. There is no guarantee that a result will ever be returned.
134- *
135- * It is the caller's responsibility to guard against this function not returning a result in a timely manner, or not returning a result ever.
136- */
137- override suspend fun waitForReferrerCode (): ParsedReferrerResult {
138- if (referralResult != ReferrerInitialising ) {
139- Timber .d(" Referrer already determined; immediately answering" )
140- return referralResult
141- }
142-
143- Timber .i(" Referrer: Retrieving referral code from Play Store referrer service" )
144-
145- // poll, awaiting referral result to become available
146- while (referralResult == ReferrerInitialising ) {
147- Timber .v(" Still initialising - waiting" )
148- delay(10 )
149- }
150-
151- return referralResult
152- }
153-
154- private fun loadFromDataStore (): String? {
155- return appReferrerDataStore.campaignSuffix
156- }
157-
158- private fun playStoreReferralServiceInstalled (): Boolean {
159- val playStoreConnectionServiceIntent = Intent ()
160- playStoreConnectionServiceIntent.component = ComponentName (PLAY_STORE_PACKAGE , PLAY_STORE_REFERRAL_SERVICE )
161- val matchingServices = packageManager.queryIntentServices(playStoreConnectionServiceIntent, 0 )
162- return matchingServices.size > 0
163- }
164-
165- private fun referralResultReceived (result : ParsedReferrerResult ) {
166- referralResult = result
167-
168- when (result) {
169- is CampaignReferrerFound -> {
170- variantManager.updateAppReferrerVariant(result.campaignSuffix)
171- appReferrerDataStore.campaignSuffix = result.campaignSuffix
172- }
173- is EuAuctionReferrerFound -> {
174- variantManager.updateAppReferrerVariant(VariantManager .RESERVED_EU_AUCTION_VARIANT )
175- appReferrerDataStore.installedFromEuAuction = true
176- }
177- }
178-
179- appReferrerDataStore.referrerCheckedPreviously = true
180- }
181-
182- private fun referralResultFailed (reason : ParseFailureReason ) {
183- referralResult = ParseFailure (reason)
184- }
185-
186- override fun onInstallReferrerServiceDisconnected () {
187- Timber .i(" Referrer: ServiceDisconnected" )
188- }
189-
190- override suspend fun beforeAtbInit () {
191- waitForReferrerCode()
192- }
193-
194- override fun beforeAtbInitTimeoutMillis (): Long = MAX_REFERRER_WAIT_TIME_MS
195- }
0 commit comments