Bug 1825028 - Delay init of MozillaOnline build until privacy notice accepted

Co-authored-by: Jonathan Almeida <jalmeida@mozilla.com>
fenix/113.0
Christian Sadilek 1 year ago committed by mergify[bot]
parent 8181ab1094
commit b950e808ef

@ -31,6 +31,11 @@ android {
testBuildType project.property("testBuildType")
}
// This allows overriding the target activity for MozillaOnline builds, which happens
// as part of the defaultConfig below, and applies to all other configurations (Nightly,
// Beta, and Release.)
def targetActivity = "HomeActivity"
defaultConfig {
applicationId "org.mozilla"
minSdkVersion Config.minSdkVersion
@ -64,16 +69,18 @@ android {
buildConfigField "String", "AMO_SERVER_URL", "\"https://services.addons.mozilla.org\""
def deepLinkSchemeValue = "fenix-dev"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders = [
"deepLinkScheme": deepLinkSchemeValue
]
// Build flag for "Mozilla Online" variants. See `Config.isMozillaOnline`.
if (project.hasProperty("mozillaOnline") || gradle.hasProperty("localProperties.mozillaOnline")) {
buildConfigField "boolean", "MOZILLA_ONLINE", "true"
targetActivity = "MozillaOnlineHomeActivity"
} else {
buildConfigField "boolean", "MOZILLA_ONLINE", "false"
}
manifestPlaceholders = [
"targetActivity": targetActivity,
"deepLinkScheme": deepLinkSchemeValue
]
}
def releaseTemplate = {
@ -110,7 +117,10 @@ android {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
def deepLinkSchemeValue = "fenix-nightly"
buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\""
manifestPlaceholders = ["deepLinkScheme": deepLinkSchemeValue]
manifestPlaceholders = [
"deepLinkScheme": deepLinkSchemeValue,
"targetActivity": targetActivity
]
}
beta releaseTemplate >> {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
@ -126,7 +136,8 @@ android {
// - https://issuetracker.google.com/issues/36924841
// - https://issuetracker.google.com/issues/36905922
"sharedUserId": "org.mozilla.firefox.sharedID",
"deepLinkScheme": deepLinkSchemeValue
"deepLinkScheme": deepLinkSchemeValue,
"targetActivity": targetActivity
]
}
release releaseTemplate >> {
@ -143,7 +154,8 @@ android {
// - https://issuetracker.google.com/issues/36924841
// - https://issuetracker.google.com/issues/36905922
"sharedUserId": "org.mozilla.firefox.sharedID",
"deepLinkScheme": deepLinkSchemeValue
"deepLinkScheme": deepLinkSchemeValue,
"targetActivity": targetActivity,
]
}
benchmark releaseTemplate >> {

@ -33,7 +33,7 @@ import org.mozilla.fenix.helpers.HomeActivityTestRule
*
* Say no to main thread IO! 🙅
*/
private const val EXPECTED_SUPPRESSION_COUNT = 18
private const val EXPECTED_SUPPRESSION_COUNT = 17
/**
* The number of times we call the `runBlocking` coroutine method on the main thread during this

@ -62,7 +62,7 @@
<activity-alias
android:name="${applicationId}.App"
android:exported="true"
android:targetActivity=".HomeActivity">
android:targetActivity=".${targetActivity}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -144,6 +144,9 @@
</intent-filter>
</activity>
<activity android:name=".MozillaOnlineHomeActivity"
android:exported="false"/>
<activity android:name=".home.mozonline.PrivacyContentDisplayActivity"
android:exported="false"/>

@ -11,7 +11,6 @@ import android.os.Build.VERSION.SDK_INT
import android.os.StrictMode
import android.os.SystemClock
import android.util.Log.INFO
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NotificationManagerCompat
@ -87,6 +86,7 @@ import org.mozilla.fenix.ext.isNotificationChannelEnabled
import org.mozilla.fenix.ext.setCustomEndpointIfAvailable
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.onboarding.MARKETING_CHANNEL_ID
import org.mozilla.fenix.perf.MarkersActivityLifecycleCallbacks
import org.mozilla.fenix.perf.ProfilerMarkerFactProcessor
@ -124,11 +124,24 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
private set
override fun onCreate() {
super.onCreate()
if (shouldShowPrivacyNotice()) {
// For Mozilla Online build: Delay initialization on first run until privacy notice
// is accepted by the user.
return
}
initialize()
}
/**
* Initializes Fenix and all required subsystems such as Nimbus, Glean and Gecko.
*/
fun initialize() {
// We measure ourselves to avoid a call into Glean before its loaded.
val start = SystemClock.elapsedRealtimeNanos()
super.onCreate()
setupInAllProcesses()
if (!isMainProcess()) {
@ -155,6 +168,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
}
@OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
@VisibleForTesting
protected open fun initializeGlean() {
val telemetryEnabled = settings().isTelemetryEnabled
@ -195,16 +209,16 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
)
}
@CallSuper
open fun setupInAllProcesses() {
@VisibleForTesting
protected open fun setupInAllProcesses() {
setupCrashReporting()
// We want the log messages of all builds to go to Android logcat
Log.addSink(FenixLogSink(logsDebug = Config.channel.isDebug))
}
@CallSuper
open fun setupInMainProcessOnly() {
@VisibleForTesting
protected open fun setupInMainProcessOnly() {
// ⚠️ DO NOT ADD ANYTHING ABOVE THIS LINE.
// Especially references to the engine/BrowserStore which can alter the app initialization.
// See: https://github.com/mozilla-mobile/fenix/issues/26320
@ -933,4 +947,14 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
components.useCases.wallpaperUseCases.initialize()
}
}
/**
* Checks whether or not a privacy notice needs to be displayed before
* the application can continue to initialize.
*/
internal fun shouldShowPrivacyNotice(): Boolean {
return Config.channel.isMozillaOnline &&
settings().shouldShowPrivacyPopWindow &&
!FenixOnboarding(this).userHasBeenOnboarded()
}
}

@ -0,0 +1,29 @@
/* 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/. */
package org.mozilla.fenix
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.mozilla.fenix.home.mozonline.showPrivacyPopWindow
/**
* This activity is specific to the Mozilla Online build and used to display
* a privacy notice on first run. Once the privacy notice is accepted, and for
* all subsequent launches, it will simply launch the Fenix [HomeActivity].
*/
open class MozillaOnlineHomeActivity : AppCompatActivity() {
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if ((this.application as FenixApplication).shouldShowPrivacyNotice()) {
showPrivacyPopWindow(this.applicationContext, this)
} else {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
}
}

@ -62,7 +62,6 @@ import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.service.glean.private.NoExtras
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.Config
import org.mozilla.fenix.GleanMetrics.HomeScreen
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcutCfr
import org.mozilla.fenix.HomeActivity
@ -87,7 +86,6 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.gleanplumb.DefaultMessageController
import org.mozilla.fenix.gleanplumb.MessagingFeature
import org.mozilla.fenix.gleanplumb.NimbusMessagingController
import org.mozilla.fenix.home.mozonline.showPrivacyPopWindow
import org.mozilla.fenix.home.pocket.DefaultPocketStoriesController
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.privatebrowsing.controller.DefaultPrivateBrowsingController
@ -219,13 +217,6 @@ class HomeFragment : Fragment() {
bundleArgs = args.toBundle()
if (!onboarding.userHasBeenOnboarded() &&
requireContext().settings().shouldShowPrivacyPopWindow &&
Config.channel.isMozillaOnline
) {
showPrivacyPopWindow(requireContext(), requireActivity())
}
// DO NOT MOVE ANYTHING BELOW THIS addMarker CALL!
requireComponents.core.engine.profiler?.addMarker(
MarkersFragmentLifecycleCallbacks.MARKER_NAME,

@ -5,29 +5,20 @@
package org.mozilla.fenix.home.mozonline
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.View
import android.webkit.WebView
import android.widget.ImageButton
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.contextmenu.DefaultSelectionActionDelegate
import mozilla.components.feature.search.BrowserStoreSearchAdapter
import mozilla.components.support.ktx.android.content.call
import mozilla.components.support.ktx.android.content.email
import mozilla.components.support.ktx.android.content.share
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
/**
* A special activity for displaying the detail content about privacy hyperlinked in alert dialog.
*/
class PrivacyContentDisplayActivity : Activity(), EngineSession.Observer {
private lateinit var engineView: EngineView
private lateinit var webView: WebView
private lateinit var closeButton: ImageButton
private lateinit var engineSession: EngineSession
private var url: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -37,48 +28,20 @@ class PrivacyContentDisplayActivity : Activity(), EngineSession.Observer {
url = addr.getString("url")
}
engineView = findViewById<View>(R.id.privacyContentEngineView) as EngineView
webView = findViewById<View>(R.id.privacyContentEngineView) as WebView
webView.settings.javaScriptEnabled = true
closeButton = findViewById<View>(R.id.privacyContentCloseButton) as ImageButton
engineSession = components.core.engine.createSession()
}
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet,
): View? = when (name) {
EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply {
selectionActionDelegate = DefaultSelectionActionDelegate(
BrowserStoreSearchAdapter(
components.core.store,
),
resources = context.resources,
shareTextClicked = { share(it) },
emailTextClicked = { email(it) },
callTextClicked = { call(it) },
)
}.asView()
else -> super.onCreateView(parent, name, context, attrs)
}
override fun onStart() {
super.onStart()
engineSession.register(this)
engineSession.let { engineSession ->
engineView.render(engineSession)
url?.let { engineSession.loadUrl(it) }
}
closeButton.setOnClickListener { finish() }
}
override fun onStop() {
super.onStop()
engineSession.unregister(this)
}
url?.let {
webView.loadUrl(it)
}
override fun onDestroy() {
super.onDestroy()
engineSession.close()
closeButton.setOnClickListener {
finish()
}
}
}

@ -6,13 +6,16 @@ package org.mozilla.fenix.home.mozonline
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.text.SpannableString
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import kotlin.system.exitProcess
@ -57,6 +60,10 @@ fun showPrivacyPopWindow(context: Context, activity: Activity) {
context.settings().shouldShowPrivacyPopWindow = false
context.settings().isMarketingTelemetryEnabled = true
context.components.analytics.metrics.start(MetricServiceType.Marketing)
// Now that the privacy notice is accepted, application initialization can continue.
context.application.initialize()
activity.startActivity(Intent(activity, HomeActivity::class.java))
activity.finish()
}
.setNeutralButton(
context.getString(R.string.privacy_notice_neutral_button_2),

@ -33,7 +33,7 @@ private val mainLooper = Looper.getMainLooper()
* Manages strict mode settings for the application.
*/
open class StrictModeManager(
config: Config,
private val config: Config,
// Ideally, we'd pass in a more specific value but there is a circular dependency: StrictMode
// is passed into Core but we'd need to pass in Core here. Instead, we take components and later
@ -112,6 +112,14 @@ open class StrictModeManager(
*/
open fun <R> resetAfter(policy: StrictMode.ThreadPolicy, functionBlock: () -> R): R {
fun instrumentedFunctionBlock(): R {
// For Mozilla Online builds, we decided not to add a profile marker because we need to
// prevent early initialization of the engine. These markers also have no distinct
// meaning to the Mozilla Online build variant.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1825028
if (config.channel.isMozillaOnline) {
return functionBlock()
}
val startProfilerTime = components.core.engine.profiler?.getProfilerTime()
val returnValue = functionBlock()

@ -35,7 +35,7 @@
</LinearLayout>
<mozilla.components.concept.engine.EngineView
<WebView
tools:ignore="Instantiatable"
android:id="@+id/privacyContentEngineView"
android:layout_width="match_parent"

Loading…
Cancel
Save