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/JunoOnboardingFragment.kt

230 lines
9.2 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.appwidget.AppWidgetManager
import android.content.ComponentName
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.compose.ui.platform.LocalContext
import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.fragment.findNavController
import mozilla.components.support.base.ext.areNotificationsEnabledSafe
import org.mozilla.fenix.R
import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint
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.JunoOnboardingScreen
import org.mozilla.fenix.onboarding.view.OnboardingPageUiData
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.gecko.search.SearchWidgetProvider
/**
* Fragment displaying the juno onboarding flow.
*/
class JunoOnboardingFragment : Fragment() {
private val pagesToDisplay by lazy {
pagesToDisplay(
canShowNotificationPage(requireContext()),
canShowAddWidgetCard(),
)
}
private val telemetryRecorder by lazy { JunoOnboardingTelemetryRecorder() }
private val pinAppWidgetReceiver = WidgetPinnedReceiver()
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isNotATablet()) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
val filter = IntentFilter(WidgetPinnedReceiver.ACTION)
LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(pinAppWidgetReceiver, filter)
}
@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() {
val context = LocalContext.current
JunoOnboardingScreen(
pagesToDisplay = pagesToDisplay,
onMakeFirefoxDefaultClick = {
activity?.openSetDefaultBrowserOption(useCustomTab = true)
telemetryRecorder.onSetToDefaultClick(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition = pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
},
onSkipDefaultClick = {
telemetryRecorder.onSkipSetToDefaultClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
},
onPrivacyPolicyClick = { url ->
startActivity(
SupportUtils.createSandboxCustomTabIntent(
context = context,
url = url,
),
)
telemetryRecorder.onPrivacyPolicyClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.DEFAULT_BROWSER),
)
},
onSignInButtonClick = {
findNavController().nav(
id = R.id.junoOnboardingFragment,
directions = JunoOnboardingFragmentDirections.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),
)
showAddSearchWidgetDialog()
},
onSkipFirefoxWidgetClick = {
telemetryRecorder.onSkipAddWidgetClick(
pagesToDisplay.telemetrySequenceId(),
pagesToDisplay.sequencePosition(OnboardingPageUiData.Type.ADD_SEARCH_WIDGET),
)
},
onFinish = {
onFinish(
sequenceId = pagesToDisplay.telemetrySequenceId(),
sequencePosition = pagesToDisplay.sequencePosition(it.type),
)
},
onImpression = {
telemetryRecorder.onImpression(
sequenceId = pagesToDisplay.telemetrySequenceId(),
pageType = it.type,
sequencePosition = pagesToDisplay.sequencePosition(it.type),
)
},
)
}
private fun showAddSearchWidgetDialog() {
// Requesting to pin app widget is only available for Android 8.0 and above
if (canShowAddWidgetCard()) {
val appWidgetManager = AppWidgetManager.getInstance(activity)
val searchWidgetProvider =
ComponentName(requireActivity(), SearchWidgetProvider::class.java)
if (appWidgetManager.isRequestPinAppWidgetSupported) {
val successCallback = WidgetPinnedReceiver.getPendingIntent(requireContext())
appWidgetManager.requestPinAppWidget(searchWidgetProvider, null, successCallback)
}
}
}
private fun onFinish(sequenceId: String, sequencePosition: String) {
requireComponents.fenixOnboarding.finish()
findNavController().nav(
id = R.id.junoOnboardingFragment,
directions = JunoOnboardingFragmentDirections.actionHome(),
)
telemetryRecorder.onOnboardingComplete(
sequenceId = sequenceId,
sequencePosition = sequencePosition,
)
}
private fun canShowNotificationPage(context: Context) =
!NotificationManagerCompat.from(context.applicationContext)
.areNotificationsEnabledSafe() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
private fun canShowAddWidgetCard() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
private fun isNotATablet() = !resources.getBoolean(R.bool.tablet)
private fun pagesToDisplay(
showNotificationPage: Boolean,
showAddWidgetPage: Boolean,
): List<OnboardingPageUiData> =
FxNimbus.features.junoOnboarding.value().cards.values.toPageUiData(
showNotificationPage,
showAddWidgetPage,
)
}