Skip to content

Commit

Permalink
Add SelectAccountScreen (#836)
Browse files Browse the repository at this point in the history
  • Loading branch information
luizgrp authored Dec 14, 2022
1 parent c0ce58b commit 1906b9c
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 18 deletions.
17 changes: 16 additions & 1 deletion auth-composables/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,24 @@ package com.google.android.horologist.auth.composables.chips {
package com.google.android.horologist.auth.composables.dialogs {

public final class SignedInConfirmationDialogKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public static void SignedInConfirmationDialog(optional androidx.compose.ui.Modifier modifier, optional String? name, optional String? email, optional Object? avatarUri, optional java.time.Duration duration, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissOrTimeout);
method @androidx.compose.runtime.Composable @com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public static void SignedInConfirmationDialog(optional androidx.compose.ui.Modifier modifier, optional String? name, optional String? email, optional Object? avatar, optional java.time.Duration duration, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissOrTimeout);
}

}

package com.google.android.horologist.auth.composables.screens {

@com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public final class AccountUiModel {
ctor public AccountUiModel(String email, optional Object? avatar);
method public String component1();
method public Object? component2();
method public com.google.android.horologist.auth.composables.screens.AccountUiModel copy(String email, Object? avatar);
method public Object? getAvatar();
method public String getEmail();
property public final Object? avatar;
property public final String email;
}

public final class AuthErrorScreenKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public static void AuthErrorScreen(optional androidx.compose.ui.Modifier modifier);
}
Expand All @@ -45,5 +56,9 @@ package com.google.android.horologist.auth.composables.screens {
method @androidx.compose.runtime.Composable @com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public static void CheckYourPhoneScreen(optional androidx.compose.ui.Modifier modifier, String message);
}

public final class SelectAccountScreenKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi public static void SelectAccountScreen(java.util.List<com.google.android.horologist.auth.composables.screens.AccountUiModel> accounts, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super com.google.android.horologist.auth.composables.screens.AccountUiModel,kotlin.Unit> onAccountClicked, com.google.android.horologist.compose.layout.ScalingLazyColumnState columnState, optional androidx.compose.ui.Modifier modifier, optional String title, optional Object? defaultAvatar);
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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.
*/

@file:OptIn(ExperimentalHorologistAuthComposablesApi::class)

package com.google.android.horologist.auth.composables.screens

import androidx.compose.runtime.Composable
import com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi
import com.google.android.horologist.compose.layout.belowTimeTextPreview
import com.google.android.horologist.compose.tools.WearPreviewDevices

@WearPreviewDevices
@Composable
fun SelectAccountScreenPreview() {
SelectAccountScreen(
accounts = listOf(
AccountUiModel(email = "maggie@example.com"),
AccountUiModel(email = "thisisaverylongemail@example.com")
),
onAccountClicked = { _, _ -> },
columnState = belowTimeTextPreview()
)
}

@WearPreviewDevices
@Composable
fun SelectAccountScreenPreviewNoAvatar() {
SelectAccountScreen(
accounts = listOf(
AccountUiModel(email = "maggie@example.com"),
AccountUiModel(email = "thisisaverylongemailaccountsample@example.com")
),
onAccountClicked = { _, _ -> },
columnState = belowTimeTextPreview(),
defaultAvatar = null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public fun SignedInConfirmationDialog(
modifier: Modifier = Modifier,
name: String? = null,
email: String? = null,
avatarUri: Any? = null,
avatar: Any? = null,
duration: Duration = Duration.ofMillis(DialogDefaults.ShortDurationMillis),
onDismissOrTimeout: () -> Unit
) {
Expand All @@ -75,7 +75,7 @@ public fun SignedInConfirmationDialog(
modifier = modifier,
displayName = name,
email = email,
avatarUri = avatarUri
avatar = avatar
)
}
}
Expand All @@ -86,7 +86,7 @@ internal fun SignedInConfirmationDialogContent(
modifier: Modifier = Modifier,
displayName: String? = null,
email: String? = null,
avatarUri: Any? = null
avatar: Any? = null
) {
val configuration = LocalConfiguration.current
val horizontalPadding = (configuration.screenWidthDp * HORIZONTAL_PADDING_SCREEN_PERCENTAGE).dp
Expand All @@ -101,7 +101,7 @@ internal fun SignedInConfirmationDialogContent(
horizontalAlignment = Alignment.CenterHorizontally
) {
val hasName = displayName != null
val hasAvatar = avatarUri != null
val hasAvatar = avatar != null

Box(
modifier = Modifier
Expand All @@ -112,7 +112,7 @@ internal fun SignedInConfirmationDialogContent(
if (hasAvatar) {
Image(
modifier = Modifier.clip(CircleShape),
painter = rememberAsyncImagePainter(model = avatarUri),
painter = rememberAsyncImagePainter(model = avatar),
contentDescription = DECORATIVE_ELEMENT_CONTENT_DESCRIPTION,
contentScale = ContentScale.Fit
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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 com.google.android.horologist.auth.composables.screens

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi
import com.google.android.horologist.auth.composables.R
import com.google.android.horologist.base.ui.components.StandardChip
import com.google.android.horologist.base.ui.components.StandardChipType
import com.google.android.horologist.base.ui.components.Title
import com.google.android.horologist.compose.layout.ScalingLazyColumn
import com.google.android.horologist.compose.layout.ScalingLazyColumnState

private const val HORIZONTAL_PADDING_SCREEN_PERCENTAGE = 0.052

@ExperimentalHorologistAuthComposablesApi
@Composable
public fun SelectAccountScreen(
accounts: List<AccountUiModel>,
onAccountClicked: (index: Int, account: AccountUiModel) -> Unit,
columnState: ScalingLazyColumnState,
modifier: Modifier = Modifier,
title: String = stringResource(id = R.string.horologist_select_account_title),
defaultAvatar: Any? = Icons.Default.AccountCircle
) {
val configuration = LocalConfiguration.current
val horizontalPadding = (configuration.screenWidthDp * HORIZONTAL_PADDING_SCREEN_PERCENTAGE).dp

ScalingLazyColumn(
modifier = modifier
.fillMaxSize()
.padding(horizontal = horizontalPadding),
columnState = columnState
) {
item { Title(text = title, modifier = Modifier.padding(bottom = 8.dp)) }

items(accounts.size) { index ->
val account = accounts[index]

StandardChip(
label = account.email,
icon = account.avatar ?: defaultAvatar,
largeIcon = true,
onClick = { onAccountClicked(index, account) },
chipType = StandardChipType.Secondary
)
}
}
}

@ExperimentalHorologistAuthComposablesApi
public data class AccountUiModel(
val email: String,
val avatar: Any? = null
)
1 change: 1 addition & 0 deletions auth-composables/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
<string name="horologist_guest_mode_chip_label">Use without account</string>
<string name="horologist_other_options_chip_label">Other options</string>
<string name="horologist_create_account_chip_label">Create account</string>
<string name="horologist_select_account_title">Select account</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class SignedInConfirmationDialogTest {
SignedInConfirmationDialogContent(
displayName = "Maggie",
email = "maggie@example.com",
avatarUri = android.R.drawable.sym_def_app_icon
avatar = android.R.drawable.sym_def_app_icon
)
}
}
Expand All @@ -66,7 +66,7 @@ class SignedInConfirmationDialogTest {
) {
SignedInConfirmationDialogContent(
email = "maggie@example.com",
avatarUri = android.R.drawable.sym_def_app_icon
avatar = android.R.drawable.sym_def_app_icon
)
}
}
Expand Down Expand Up @@ -97,7 +97,7 @@ class SignedInConfirmationDialogTest {
) {
SignedInConfirmationDialogContent(
displayName = "Maggie",
avatarUri = android.R.drawable.sym_def_app_icon
avatar = android.R.drawable.sym_def_app_icon
)
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ class SignedInConfirmationDialogTest {
SignedInConfirmationDialogContent(
displayName = "Wolfeschlegelsteinhausenbergerdorff",
email = "wolfeschlegelsteinhausenbergerdorff@example.com",
avatarUri = android.R.drawable.sym_def_app_icon
avatar = android.R.drawable.sym_def_app_icon
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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.
*/

@file:OptIn(
ExperimentalHorologistPaparazziApi::class,
ExperimentalHorologistAuthComposablesApi::class
)

package com.google.android.horologist.auth.composables.screens

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Face
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.android.horologist.auth.composables.ExperimentalHorologistAuthComposablesApi
import com.google.android.horologist.compose.tools.coil.FakeImageLoader
import com.google.android.horologist.paparazzi.ExperimentalHorologistPaparazziApi
import com.google.android.horologist.paparazzi.WearPaparazzi
import com.google.android.horologist.test.toolbox.positionedState
import org.junit.Rule
import org.junit.Test

class SelectAccountScreenTest {

@get:Rule
val paparazzi = WearPaparazzi()

@Test
fun selectAccountScreen() {
paparazzi.snapshot {
FakeImageLoader.Resources.override {
Box(
modifier = Modifier.background(Color.Black),
contentAlignment = Alignment.Center
) {
SelectAccountScreen(
accounts = listOf(
AccountUiModel(
email = "maggie@example.com",
avatar = Icons.Default.Face
),
AccountUiModel(email = "thisisaverylongemail@example.com")
),
onAccountClicked = { _, _ -> },
columnState = positionedState(0, 0)
)
}
}
}
}

@Test
fun selectAccountScreenNoAvatar() {
paparazzi.snapshot {
FakeImageLoader.Resources.override {
Box(
modifier = Modifier.background(Color.Black),
contentAlignment = Alignment.Center
) {
SelectAccountScreen(
accounts = listOf(
AccountUiModel(email = "maggie@example.com"),
AccountUiModel(email = "thisisaverylongemailaccountsample@example.com")
),
onAccountClicked = { _, _ -> },
columnState = positionedState(0, 0),
defaultAvatar = null
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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 com.google.android.horologist.test.toolbox

import androidx.compose.runtime.Composable
import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.google.android.horologist.compose.tools.a11y.forceState

@Composable
fun positionedState(
topIndex: Int,
topScrollOffset: Int
): ScalingLazyColumnState {
return ScalingLazyColumnDefaults.belowTimeText().create().apply {
state.forceState(topIndex, topScrollOffset)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1906b9c

Please sign in to comment.