You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iceraven-browser/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingFragment.kt

259 lines
10 KiB
Kotlin

/* 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.onboarding
import android.annotation.SuppressLint
import android.content.Context
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.fragment.findNavController
import mozilla.components.service.nimbus.evalJexlSafe
import mozilla.components.service.nimbus.messaging.use
import mozilla.components.support.base.ext.areNotificationsEnabledSafe
import mozilla.components.support.utils.BrowsersCache
import org.mozilla.fenix.R
import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint
import org.mozilla.fenix.compose.LinkTextState
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.hideToolbar
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.openSetDefaultBrowserOption
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.onboarding.view.Caption
import org.mozilla.fenix.onboarding.view.OnboardingPageUiData
import org.mozilla.fenix.onboarding.view.OnboardingScreen
import org.mozilla.fenix.onboarding.view.sequencePosition
import org.mozilla.fenix.onboarding.view.telemetrySequenceId
import org.mozilla.fenix.onboarding.view.toPageUiData
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.utils.canShowAddSearchWidgetPrompt
import org.mozilla.fenix.utils.showAddSearchWidgetPrompt
/**
* Fragment displaying the onboarding flow.
*/
class OnboardingFragment : Fragment() {
private val pagesToDisplay by lazy {
pagesToDisplay(
isNotDefaultBrowser(requireContext()),
canShowNotificationPage(requireContext()),
canShowAddSearchWidgetPrompt(),
)
}
private val telemetryRecorder by lazy { OnboardingTelemetryRecorder() }
private val pinAppWidgetReceiver = WidgetPinnedReceiver()
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val context = requireContext()
if (pagesToDisplay.isEmpty()) {
/* do not continue if there's no onboarding pages to display */
onFinish(null)
}
if (isNotATablet()) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
val filter = IntentFilter(WidgetPinnedReceiver.ACTION)
LocalBroadcastManager.getInstance(context)
.registerReceiver(pinAppWidgetReceiver, filter)
if (isNotDefaultBrowser(context) &&
pagesToDisplay.none { it.type == OnboardingPageUiData.Type.DEFAULT_BROWSER }
) {
promptToSetAsDefaultBrowser()
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View = ComposeView(requireContext()).apply {
setContent {
FirefoxTheme {
ScreenContent()
}
}
}
override fun onResume() {
super.onResume()
hideToolbar()
}
override fun onDestroy() {
super.onDestroy()
if (isNotATablet()) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(pinAppWidgetReceiver)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
@Suppress("LongMethod")
private fun ScreenContent() {
OnboardingScreen(
pagesToDisplay = pagesToDisplay,
onMakeFirefoxDefaultClick = {
promptToSetAsDefaultBrowser()
},
onSkipDefaultClick = {
telemetryRecorder.onSkipSetToDefaultClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
},
onSignInButtonClick = {
findNavController().nav(
id = R.id.onboardingFragment,
directions = OnboardingFragmentDirections.actionGlobalTurnOnSync(
entrypoint = FenixFxAEntryPoint.NewUserOnboarding,
),
)
telemetryRecorder.onSyncSignInClick(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition = pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.SYNC_SIGN_IN),
)
},
onSkipSignInClick = {
telemetryRecorder.onSkipSignInClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.SYNC_SIGN_IN),
)
},
onNotificationPermissionButtonClick = {
requireComponents.notificationsDelegate.requestNotificationPermission()
telemetryRecorder.onNotificationPermissionClick(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition =
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.NOTIFICATION_PERMISSION),
)
},
onSkipNotificationClick = {
telemetryRecorder.onSkipTurnOnNotificationsClick(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition =
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.NOTIFICATION_PERMISSION),
)
},
onAddFirefoxWidgetClick = {
telemetryRecorder.onAddSearchWidgetClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.ADD_SEARCH_WIDGET),
)
showAddSearchWidgetPrompt(requireActivity())
},
onSkipFirefoxWidgetClick = {
telemetryRecorder.onSkipAddWidgetClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.ADD_SEARCH_WIDGET),
)
},
onFinish = {
onFinish(it)
},
onImpression = {
telemetryRecorder.onImpression(
sequenceId = pagesToDisplay.telemetrySequenceId(),
pageType = it.type,
sequencePosition = pagesToDisplay.sequencePosition(it.type),
)
},
)
}
private fun onFinish(onboardingPageUiData: OnboardingPageUiData?) {
/* onboarding page UI data can be null if there was no pages to display */
if (onboardingPageUiData != null) {
val sequenceId = pagesToDisplay.telemetrySequenceId()
val sequencePosition = pagesToDisplay.sequencePosition(onboardingPageUiData.type)
telemetryRecorder.onOnboardingComplete(
sequenceId = sequenceId,
sequencePosition = sequencePosition,
)
}
requireComponents.fenixOnboarding.finish()
findNavController().nav(
id = R.id.onboardingFragment,
directions = OnboardingFragmentDirections.actionHome(),
)
}
private fun isNotDefaultBrowser(context: Context) =
!BrowsersCache.all(context.applicationContext).isDefaultBrowser
private fun canShowNotificationPage(context: Context) =
!NotificationManagerCompat.from(context.applicationContext)
.areNotificationsEnabledSafe() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
private fun isNotATablet() = !resources.getBoolean(R.bool.tablet)
private fun pagesToDisplay(
showDefaultBrowserPage: Boolean,
showNotificationPage: Boolean,
showAddWidgetPage: Boolean,
): List<OnboardingPageUiData> {
val jexlConditions = FxNimbus.features.junoOnboarding.value().conditions
val jexlHelper = requireContext().components.nimbus.createJexlHelper()
val privacyCaption = Caption(
text = getString(R.string.juno_onboarding_privacy_notice_text),
linkTextState = LinkTextState(
text = getString(R.string.juno_onboarding_privacy_notice_text),
url = SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE),
onClick = {
startActivity(
SupportUtils.createSandboxCustomTabIntent(
context = requireContext(),
url = it,
),
)
telemetryRecorder.onPrivacyPolicyClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
},
),
)
return jexlHelper.use {
FxNimbus.features.junoOnboarding.value().cards.values.toPageUiData(
privacyCaption,
showDefaultBrowserPage,
showNotificationPage,
showAddWidgetPage,
jexlConditions,
) { condition -> jexlHelper.evalJexlSafe(condition) }
}
}
private fun promptToSetAsDefaultBrowser() {
activity?.openSetDefaultBrowserOption(useCustomTab = true)
telemetryRecorder.onSetToDefaultClick(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition = pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
}
}