|
|
@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|
|
|
import androidx.compose.foundation.layout.height
|
|
|
|
import androidx.compose.foundation.layout.height
|
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
import androidx.compose.foundation.rememberScrollState
|
|
|
|
import androidx.compose.foundation.rememberScrollState
|
|
|
|
|
|
|
|
import androidx.compose.foundation.text.ClickableText
|
|
|
|
import androidx.compose.foundation.verticalScroll
|
|
|
|
import androidx.compose.foundation.verticalScroll
|
|
|
|
import androidx.compose.material.Icon
|
|
|
|
import androidx.compose.material.Icon
|
|
|
|
import androidx.compose.material.IconButton
|
|
|
|
import androidx.compose.material.IconButton
|
|
|
@ -24,6 +25,9 @@ import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
|
|
|
|
import androidx.compose.ui.text.AnnotatedString
|
|
|
|
|
|
|
|
import androidx.compose.ui.text.SpanStyle
|
|
|
|
|
|
|
|
import androidx.compose.ui.text.buildAnnotatedString
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import org.mozilla.fenix.R
|
|
|
|
import org.mozilla.fenix.R
|
|
|
@ -38,18 +42,24 @@ import org.mozilla.fenix.theme.FirefoxTheme
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private const val IMAGE_HEIGHT_RATIO = 0.4f
|
|
|
|
private const val IMAGE_HEIGHT_RATIO = 0.4f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The tag used for links in the text for annotated strings.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private const val URL_TAG = "URL_TAG"
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* A composable for displaying onboarding page content.
|
|
|
|
* A composable for displaying onboarding page content.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param pageState [OnboardingPageState] The page content that's displayed.
|
|
|
|
* @param pageState [OnboardingPageState] The page content that's displayed.
|
|
|
|
* @param onDismiss Invoked when the user clicks the close button.
|
|
|
|
|
|
|
|
* @param modifier The modifier to be applied to the Composable.
|
|
|
|
* @param modifier The modifier to be applied to the Composable.
|
|
|
|
|
|
|
|
* @param onDismiss Invoked when the user clicks the close button. This defaults to null. When null,
|
|
|
|
|
|
|
|
* it doesn't show the close button.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun OnboardingPage(
|
|
|
|
fun OnboardingPage(
|
|
|
|
pageState: OnboardingPageState,
|
|
|
|
pageState: OnboardingPageState,
|
|
|
|
onDismiss: () -> Unit,
|
|
|
|
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
|
|
|
|
onDismiss: (() -> Unit)? = null,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
BoxWithConstraints(
|
|
|
|
BoxWithConstraints(
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
@ -65,6 +75,7 @@ fun OnboardingPage(
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
|
verticalArrangement = Arrangement.SpaceBetween,
|
|
|
|
verticalArrangement = Arrangement.SpaceBetween,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
if (onDismiss != null) {
|
|
|
|
IconButton(
|
|
|
|
IconButton(
|
|
|
|
onClick = onDismiss,
|
|
|
|
onClick = onDismiss,
|
|
|
|
modifier = Modifier.align(Alignment.End),
|
|
|
|
modifier = Modifier.align(Alignment.End),
|
|
|
@ -75,6 +86,9 @@ fun OnboardingPage(
|
|
|
|
tint = FirefoxTheme.colors.iconPrimary,
|
|
|
|
tint = FirefoxTheme.colors.iconPrimary,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Spacer(Modifier)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Column(
|
|
|
|
Column(
|
|
|
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
|
|
|
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
|
|
|
@ -98,11 +112,9 @@ fun OnboardingPage(
|
|
|
|
|
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
|
|
|
|
|
|
|
|
Text(
|
|
|
|
DescriptionText(
|
|
|
|
text = pageState.description,
|
|
|
|
description = pageState.description,
|
|
|
|
color = FirefoxTheme.colors.textSecondary,
|
|
|
|
linkTextState = pageState.linkTextState,
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
|
|
|
|
style = FirefoxTheme.typography.body2,
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -131,6 +143,71 @@ fun OnboardingPage(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
|
|
|
private fun DescriptionText(
|
|
|
|
|
|
|
|
description: String,
|
|
|
|
|
|
|
|
linkTextState: LinkTextState?,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
if (linkTextState != null) {
|
|
|
|
|
|
|
|
LinkText(
|
|
|
|
|
|
|
|
text = description,
|
|
|
|
|
|
|
|
linkTextState = linkTextState,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Text(
|
|
|
|
|
|
|
|
text = description,
|
|
|
|
|
|
|
|
color = FirefoxTheme.colors.textSecondary,
|
|
|
|
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
|
|
|
|
style = FirefoxTheme.typography.body2,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* A composable for displaying text that contains a clickable link text.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param text The complete text.
|
|
|
|
|
|
|
|
* @param linkTextState The clickable part of the text.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
|
|
|
private fun LinkText(
|
|
|
|
|
|
|
|
text: String,
|
|
|
|
|
|
|
|
linkTextState: LinkTextState,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
val annotatedString = buildAnnotatedString {
|
|
|
|
|
|
|
|
val startIndex = text.indexOf(linkTextState.text)
|
|
|
|
|
|
|
|
val endIndex = startIndex + linkTextState.text.length
|
|
|
|
|
|
|
|
append(text)
|
|
|
|
|
|
|
|
addStyle(
|
|
|
|
|
|
|
|
style = SpanStyle(color = FirefoxTheme.colors.textAccent),
|
|
|
|
|
|
|
|
start = startIndex,
|
|
|
|
|
|
|
|
end = endIndex,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
addStringAnnotation(
|
|
|
|
|
|
|
|
tag = URL_TAG,
|
|
|
|
|
|
|
|
annotation = linkTextState.url,
|
|
|
|
|
|
|
|
start = startIndex,
|
|
|
|
|
|
|
|
end = endIndex,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ClickableText(
|
|
|
|
|
|
|
|
text = annotatedString,
|
|
|
|
|
|
|
|
style = FirefoxTheme.typography.body2.copy(
|
|
|
|
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
|
|
|
|
color = FirefoxTheme.colors.textSecondary,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
onClick = {
|
|
|
|
|
|
|
|
val range: AnnotatedString.Range<String>? =
|
|
|
|
|
|
|
|
annotatedString.getStringAnnotations(URL_TAG, it, it).firstOrNull()
|
|
|
|
|
|
|
|
range?.let { stringAnnotation ->
|
|
|
|
|
|
|
|
linkTextState.onClick(stringAnnotation.item)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@LightDarkPreview
|
|
|
|
@LightDarkPreview
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
private fun OnboardingPagePreview() {
|
|
|
|
private fun OnboardingPagePreview() {
|
|
|
|