2019-03-12 21:09:04 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2019-07-12 20:38:15 +02:00
|
|
|
|
2019-03-12 21:09:04 +01:00
|
|
|
package org.mozilla.fenix.components.metrics
|
|
|
|
|
|
|
|
import android.app.Application
|
2019-03-13 00:01:19 +01:00
|
|
|
import android.util.Log
|
2019-03-12 21:09:04 +01:00
|
|
|
import com.leanplum.Leanplum
|
|
|
|
import com.leanplum.LeanplumActivityHelper
|
|
|
|
import com.leanplum.annotations.Parser
|
2019-04-03 20:59:08 +02:00
|
|
|
import com.leanplum.internal.LeanplumInternal
|
2020-02-25 23:55:43 +01:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.Dispatchers.Main
|
2020-02-06 17:14:32 +01:00
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.withContext
|
2019-12-18 15:51:21 +01:00
|
|
|
import mozilla.components.support.locale.LocaleManager
|
2019-03-12 21:09:04 +01:00
|
|
|
import org.mozilla.fenix.BuildConfig
|
2020-02-06 17:14:32 +01:00
|
|
|
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts
|
2019-09-10 19:11:58 +02:00
|
|
|
import org.mozilla.fenix.ext.settings
|
2019-12-18 15:51:21 +01:00
|
|
|
import java.util.Locale
|
2020-07-30 19:28:26 +02:00
|
|
|
import java.util.MissingResourceException
|
2019-08-22 15:47:53 +02:00
|
|
|
import java.util.UUID.randomUUID
|
2019-03-12 21:09:04 +01:00
|
|
|
|
2019-03-18 22:54:36 +01:00
|
|
|
private val Event.name: String?
|
2019-03-12 21:09:04 +01:00
|
|
|
get() = when (this) {
|
2019-03-19 00:09:27 +01:00
|
|
|
is Event.AddBookmark -> "E_Add_Bookmark"
|
|
|
|
is Event.RemoveBookmark -> "E_Remove_Bookmark"
|
|
|
|
is Event.OpenedBookmark -> "E_Opened_Bookmark"
|
|
|
|
is Event.OpenedApp -> "E_Opened_App"
|
|
|
|
is Event.OpenedAppFirstRun -> "E_Opened_App_FirstRun"
|
|
|
|
is Event.InteractWithSearchURLArea -> "E_Interact_With_Search_URL_Area"
|
2019-08-14 22:48:45 +02:00
|
|
|
is Event.CollectionSaved -> "E_Collection_Created"
|
|
|
|
is Event.CollectionTabRestored -> "E_Collection_Tab_Opened"
|
|
|
|
is Event.SyncAuthSignIn -> "E_Sign_In_FxA"
|
|
|
|
is Event.SyncAuthSignOut -> "E_Sign_Out_FxA"
|
|
|
|
is Event.ClearedPrivateData -> "E_Cleared_Private_Data"
|
2019-08-20 00:25:48 +02:00
|
|
|
is Event.DismissedOnboarding -> "E_Dismissed_Onboarding"
|
2020-02-13 20:16:02 +01:00
|
|
|
is Event.FennecToFenixMigrated -> "E_Fennec_To_Fenix_Migrated"
|
2020-07-13 00:47:34 +02:00
|
|
|
is Event.AddonInstalled -> "E_Addon_Installed"
|
2020-07-27 23:10:30 +02:00
|
|
|
is Event.SearchWidgetInstalled -> "E_Search_Widget_Added"
|
2020-07-27 23:24:02 +02:00
|
|
|
is Event.ChangedToDefaultBrowser -> "E_Changed_Default_To_Fenix"
|
2020-07-29 18:37:08 +02:00
|
|
|
is Event.TrackingProtectionSettingChanged -> "E_Changed_ETP"
|
2019-03-18 22:54:36 +01:00
|
|
|
|
2019-04-03 01:47:11 +02:00
|
|
|
// Do not track other events in Leanplum
|
2020-07-13 00:47:34 +02:00
|
|
|
else -> null
|
2019-03-19 00:09:27 +01:00
|
|
|
}
|
2019-03-12 21:09:04 +01:00
|
|
|
|
|
|
|
class LeanplumMetricsService(private val application: Application) : MetricsService {
|
2020-02-25 23:55:43 +01:00
|
|
|
val scope = CoroutineScope(Dispatchers.IO)
|
|
|
|
var leanplumJob: Job? = null
|
|
|
|
|
2019-03-13 00:01:19 +01:00
|
|
|
data class Token(val id: String, val token: String) {
|
|
|
|
enum class Type { Development, Production, Invalid }
|
|
|
|
|
|
|
|
val type by lazy {
|
|
|
|
when {
|
|
|
|
token.take(ProdPrefix.length) == ProdPrefix -> Type.Production
|
|
|
|
token.take(DevPrefix.length) == DevPrefix -> Type.Development
|
|
|
|
else -> Type.Invalid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val ProdPrefix = "prod"
|
|
|
|
private const val DevPrefix = "dev"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 06:53:37 +01:00
|
|
|
override val type = MetricServiceType.Marketing
|
2019-03-13 00:01:19 +01:00
|
|
|
private val token = Token(LeanplumId, LeanplumToken)
|
|
|
|
|
2019-03-12 21:09:04 +01:00
|
|
|
override fun start() {
|
2020-02-08 02:46:51 +01:00
|
|
|
|
2020-02-25 23:55:43 +01:00
|
|
|
if (!application.settings().isMarketingTelemetryEnabled) return
|
2019-03-13 00:01:19 +01:00
|
|
|
|
2019-04-03 20:59:08 +02:00
|
|
|
Leanplum.setIsTestModeEnabled(false)
|
2019-03-12 21:09:04 +01:00
|
|
|
Leanplum.setApplicationContext(application)
|
2019-08-22 15:47:53 +02:00
|
|
|
Leanplum.setDeviceId(randomUUID().toString())
|
2019-03-12 21:09:04 +01:00
|
|
|
Parser.parseVariables(application)
|
2019-03-13 00:01:19 +01:00
|
|
|
|
2020-02-25 23:55:43 +01:00
|
|
|
leanplumJob = scope.launch {
|
2019-06-14 21:16:55 +02:00
|
|
|
|
2020-02-25 23:55:43 +01:00
|
|
|
val applicationSetLocale = LocaleManager.getCurrentLocale(application)
|
2020-07-30 19:28:26 +02:00
|
|
|
val currentLocale = applicationSetLocale ?: Locale.getDefault()
|
|
|
|
val languageCode =
|
|
|
|
currentLocale.iso3LanguageOrNull
|
|
|
|
?: currentLocale.language.let {
|
|
|
|
if (it.isNotBlank()) {
|
|
|
|
it
|
|
|
|
} else {
|
|
|
|
currentLocale.toString()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isLeanplumEnabled(languageCode)) {
|
|
|
|
Log.i(LOGTAG, "Leanplum is not available for this locale: $languageCode")
|
2020-02-25 23:55:43 +01:00
|
|
|
return@launch
|
|
|
|
}
|
2019-06-14 21:16:55 +02:00
|
|
|
|
2020-02-25 23:55:43 +01:00
|
|
|
when (token.type) {
|
|
|
|
Token.Type.Production -> Leanplum.setAppIdForProductionMode(token.id, token.token)
|
|
|
|
Token.Type.Development -> Leanplum.setAppIdForDevelopmentMode(token.id, token.token)
|
|
|
|
Token.Type.Invalid -> {
|
|
|
|
Log.i(LOGTAG, "Invalid or missing Leanplum token")
|
|
|
|
return@launch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LeanplumActivityHelper.enableLifecycleCallbacks(application)
|
|
|
|
|
|
|
|
val installedApps = MozillaProductDetector.getInstalledMozillaProducts(application)
|
|
|
|
|
2020-07-16 18:09:32 +02:00
|
|
|
val trackingProtection = application.settings().run {
|
|
|
|
when {
|
|
|
|
!shouldUseTrackingProtection -> "none"
|
|
|
|
useStandardTrackingProtection -> "standard"
|
|
|
|
useStrictTrackingProtection -> "strict"
|
|
|
|
else -> "custom"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 23:55:43 +01:00
|
|
|
Leanplum.start(application, hashMapOf(
|
|
|
|
"default_browser" to MozillaProductDetector.getMozillaBrowserDefault(application).orEmpty(),
|
|
|
|
"fennec_installed" to installedApps.contains(MozillaProducts.FIREFOX.productName),
|
|
|
|
"focus_installed" to installedApps.contains(MozillaProducts.FOCUS.productName),
|
|
|
|
"klar_installed" to installedApps.contains(MozillaProducts.KLAR.productName),
|
|
|
|
"fxa_signed_in" to application.settings().fxaSignedIn,
|
|
|
|
"fxa_has_synced_items" to application.settings().fxaHasSyncedItems,
|
|
|
|
"search_widget_installed" to application.settings().searchWidgetInstalled,
|
2020-07-16 18:09:32 +02:00
|
|
|
"tracking_protection_enabled" to application.settings().shouldUseTrackingProtection,
|
|
|
|
"tracking_protection_setting" to trackingProtection,
|
2020-02-25 23:55:43 +01:00
|
|
|
"fenix" to true
|
|
|
|
))
|
|
|
|
|
|
|
|
withContext(Main) {
|
|
|
|
LeanplumInternal.setCalledStart(true)
|
|
|
|
LeanplumInternal.setHasStarted(true)
|
|
|
|
LeanplumInternal.setStartedInBackground(true)
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 21:09:04 +01:00
|
|
|
}
|
|
|
|
|
2019-04-03 20:59:08 +02:00
|
|
|
override fun stop() {
|
2020-02-08 02:46:51 +01:00
|
|
|
if (application.settings().isMarketingTelemetryEnabled) return
|
2019-04-03 20:59:08 +02:00
|
|
|
// As written in LeanPlum SDK documentation, "This prevents Leanplum from communicating with the server."
|
|
|
|
// as this "isTestMode" flag is checked before LeanPlum SDK does anything.
|
|
|
|
// Also has the benefit effect of blocking the display of already downloaded messages.
|
|
|
|
// The reverse of this - setIsTestModeEnabled(false) must be called before trying to init
|
|
|
|
// LP in the same session.
|
|
|
|
Leanplum.setIsTestModeEnabled(true)
|
|
|
|
|
|
|
|
// This is just to allow restarting LP and it's functionality in the same app session
|
|
|
|
// as LP stores it's state internally and check against it
|
|
|
|
LeanplumInternal.setCalledStart(false)
|
|
|
|
LeanplumInternal.setHasStarted(false)
|
2020-02-25 23:55:43 +01:00
|
|
|
leanplumJob?.cancel()
|
2019-04-03 20:59:08 +02:00
|
|
|
}
|
|
|
|
|
2019-03-12 21:09:04 +01:00
|
|
|
override fun track(event: Event) {
|
2019-08-22 15:47:53 +02:00
|
|
|
val leanplumExtras = event.extras
|
|
|
|
?.map { (key, value) -> key.toString() to value }
|
|
|
|
?.toMap()
|
2019-08-20 00:25:48 +02:00
|
|
|
|
2019-03-18 22:54:36 +01:00
|
|
|
event.name?.also {
|
2019-08-20 00:25:48 +02:00
|
|
|
Leanplum.track(it, leanplumExtras)
|
2019-03-18 22:54:36 +01:00
|
|
|
}
|
2019-03-12 21:09:04 +01:00
|
|
|
}
|
|
|
|
|
2019-03-13 00:01:19 +01:00
|
|
|
override fun shouldTrack(event: Event): Boolean {
|
2019-09-24 19:33:46 +02:00
|
|
|
return application.settings().isTelemetryEnabled &&
|
2019-03-18 22:54:36 +01:00
|
|
|
token.type != Token.Type.Invalid && !event.name.isNullOrEmpty()
|
2019-03-13 00:01:19 +01:00
|
|
|
}
|
2019-03-12 21:09:04 +01:00
|
|
|
|
2019-12-18 15:51:21 +01:00
|
|
|
private fun isLeanplumEnabled(locale: String): Boolean {
|
|
|
|
return LEANPLUM_ENABLED_LOCALES.contains(locale)
|
|
|
|
}
|
|
|
|
|
2020-07-30 19:28:26 +02:00
|
|
|
private val Locale.iso3LanguageOrNull: String?
|
|
|
|
get() =
|
|
|
|
try {
|
|
|
|
this.isO3Language
|
|
|
|
} catch (_: MissingResourceException) { null }
|
|
|
|
|
2019-03-12 21:09:04 +01:00
|
|
|
companion object {
|
2019-03-13 00:01:19 +01:00
|
|
|
private const val LOGTAG = "LeanplumMetricsService"
|
|
|
|
|
2019-03-12 21:09:04 +01:00
|
|
|
private val LeanplumId: String
|
2019-08-08 20:53:05 +02:00
|
|
|
// Debug builds have a null (nullable) LEANPLUM_ID
|
2019-09-14 20:10:11 +02:00
|
|
|
get() = BuildConfig.LEANPLUM_ID.orEmpty()
|
2019-03-12 21:09:04 +01:00
|
|
|
private val LeanplumToken: String
|
2019-08-08 20:53:05 +02:00
|
|
|
// Debug builds have a null (nullable) LEANPLUM_TOKEN
|
2019-09-14 20:10:11 +02:00
|
|
|
get() = BuildConfig.LEANPLUM_TOKEN.orEmpty()
|
2019-12-18 15:51:21 +01:00
|
|
|
// Leanplum needs to be enabled for the following locales.
|
|
|
|
// Irrespective of the actual device location.
|
2020-07-30 19:28:26 +02:00
|
|
|
private val LEANPLUM_ENABLED_LOCALES = setOf(
|
2019-12-18 15:51:21 +01:00
|
|
|
"eng", // English
|
|
|
|
"zho", // Chinese
|
|
|
|
"deu", // German
|
|
|
|
"fra", // French
|
|
|
|
"ita", // Italian
|
|
|
|
"ind", // Indonesian
|
|
|
|
"por", // Portuguese
|
|
|
|
"spa", // Spanish; Castilian
|
|
|
|
"pol", // Polish
|
|
|
|
"rus", // Russian
|
|
|
|
"hin", // Hindi
|
|
|
|
"per", // Persian
|
|
|
|
"fas", // Persian
|
|
|
|
"ara", // Arabic
|
|
|
|
"jpn" // Japanese
|
|
|
|
)
|
2019-03-12 21:09:04 +01:00
|
|
|
}
|
|
|
|
}
|