Sign in with QR code (#2793)
* Add QR code login. * Add FF to disable it in release mode. * Force portrait orientation on the login flow. * Create `NumberedList` UI components. * Improve camera permission dialog. * Make nodes in qrcode feature use `QrCodeLoginScope` instead of `AppScope` * Bump SDK version. * Fix maestro tests --------- Co-authored-by: Benoit Marty <benoit@matrix.org> Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
e0c55ff4c8
commit
35702c04e9
253 changed files with 4421 additions and 326 deletions
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.atomic.molecules
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.modifiers.squareSize
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
@Composable
|
||||
fun NumberedListMolecule(
|
||||
index: Int,
|
||||
text: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
ItemNumber(index = index)
|
||||
Text(text = text, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ItemNumber(
|
||||
index: Int,
|
||||
) {
|
||||
val color = ElementTheme.colors.textPlaceholder
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.border(1.dp, color, CircleShape)
|
||||
.squareSize()
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(1.5.dp),
|
||||
text = index.toString(),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.atomic.organisms
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.NumberedListMolecule
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun NumberedListOrganism(
|
||||
items: ImmutableList<AnnotatedString>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
itemsIndexed(items) { index, item ->
|
||||
NumberedListMolecule(index = index + 1, text = item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,12 +25,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
import io.element.android.libraries.designsystem.components.PageTitle
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
|
@ -49,7 +49,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FlowStepPage(
|
||||
iconVector: ImageVector?,
|
||||
iconStyle: BigIcon.Style,
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
|
|
@ -73,10 +73,10 @@ fun FlowStepPage(
|
|||
)
|
||||
},
|
||||
header = {
|
||||
IconTitleSubtitleMolecule(
|
||||
iconImageVector = iconVector,
|
||||
PageTitle(
|
||||
title = title,
|
||||
subTitle = subTitle,
|
||||
subtitle = subTitle,
|
||||
iconStyle = iconStyle,
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
|
|
@ -97,7 +97,7 @@ internal fun FlowStepPagePreview() = ElementPreview {
|
|||
onBackClick = {},
|
||||
title = "Title",
|
||||
subTitle = "Subtitle",
|
||||
iconVector = CompoundIcons.Computer(),
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.Computer()),
|
||||
buttons = {
|
||||
TextButton(text = "A button", onClick = { })
|
||||
Button(text = "Continue", onClick = { })
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ fun ConfirmationDialog(
|
|||
thirdButtonText: String? = null,
|
||||
onCancelClick: () -> Unit = onDismiss,
|
||||
onThirdButtonClick: () -> Unit = {},
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) {
|
||||
ConfirmationDialogContent(
|
||||
|
|
@ -56,6 +57,7 @@ fun ConfirmationDialog(
|
|||
onSubmitClick = onSubmitClick,
|
||||
onCancelClick = onCancelClick,
|
||||
onThirdButtonClick = onThirdButtonClick,
|
||||
icon = icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.modifiers
|
||||
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import io.element.android.libraries.designsystem.text.toPx
|
||||
|
||||
/**
|
||||
* Draw a border on corners around the content.
|
||||
*/
|
||||
@Suppress("ModifierComposed")
|
||||
fun Modifier.cornerBorder(
|
||||
strokeWidth: Dp,
|
||||
color: Color,
|
||||
cornerSizeDp: Dp,
|
||||
) = composed(
|
||||
factory = {
|
||||
val strokeWidthPx = strokeWidth.toPx()
|
||||
val cornerSize = cornerSizeDp.toPx()
|
||||
drawWithContent {
|
||||
drawContent()
|
||||
val width = size.width
|
||||
val height = size.height
|
||||
drawPath(
|
||||
path = Path().apply {
|
||||
// Top left corner
|
||||
moveTo(0f, cornerSize)
|
||||
lineTo(0f, 0f)
|
||||
lineTo(cornerSize, 0f)
|
||||
// Top right corner
|
||||
moveTo(width - cornerSize, 0f)
|
||||
lineTo(width, 0f)
|
||||
lineTo(width, cornerSize)
|
||||
// Bottom right corner
|
||||
moveTo(width, height - cornerSize)
|
||||
lineTo(width, height)
|
||||
lineTo(width - cornerSize, height)
|
||||
// Bottom left corner
|
||||
moveTo(cornerSize, height)
|
||||
lineTo(0f, height)
|
||||
lineTo(0f, height - cornerSize)
|
||||
},
|
||||
color = color,
|
||||
style = Stroke(
|
||||
width = strokeWidthPx,
|
||||
pathEffect = PathEffect.cornerPathEffect(strokeWidthPx / 2),
|
||||
cap = StrokeCap.Round,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -37,6 +37,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -150,6 +151,7 @@ internal fun SimpleAlertDialogContent(
|
|||
Text(
|
||||
text = titleText,
|
||||
style = ElementTheme.typography.fontHeadingSmMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
||||
@Composable
|
||||
fun annotatedTextWithBold(text: String, boldText: String): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
append(text)
|
||||
val start = text.indexOf(boldText)
|
||||
val end = start + boldText.length
|
||||
val textRange = 0..text.length
|
||||
if (start in textRange && end in textRange) {
|
||||
addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun ForceOrientation(orientation: ScreenOrientation) {
|
||||
val activity = LocalContext.current as? ComponentActivity ?: return
|
||||
val orientationFlags = when (orientation) {
|
||||
ScreenOrientation.PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
ScreenOrientation.LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
}
|
||||
DisposableEffect(orientation) {
|
||||
activity.requestedOrientation = orientationFlags
|
||||
onDispose { activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED }
|
||||
}
|
||||
}
|
||||
|
||||
enum class ScreenOrientation {
|
||||
PORTRAIT,
|
||||
LANDSCAPE
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
|
||||
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun ForceOrientationInMobileDevices(orientation: ScreenOrientation) {
|
||||
val windowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||
if (windowAdaptiveInfo.windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact ||
|
||||
windowAdaptiveInfo.windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact
|
||||
) {
|
||||
ForceOrientation(orientation = orientation)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue