diff --git a/profile/src/main/java/org/openedx/profile/data/model/Account.kt b/profile/src/main/java/org/openedx/profile/data/model/Account.kt index ff069376a..88f735c5b 100644 --- a/profile/src/main/java/org/openedx/profile/data/model/Account.kt +++ b/profile/src/main/java/org/openedx/profile/data/model/Account.kt @@ -3,7 +3,7 @@ package org.openedx.profile.data.model import com.google.gson.annotations.SerializedName import org.openedx.core.data.model.ProfileImage import org.openedx.profile.domain.model.Account -import java.util.* +import java.util.Date import org.openedx.profile.domain.model.Account as DomainAccount data class Account( @@ -42,16 +42,17 @@ data class Account( ) { enum class Privacy { - @SerializedName("private") + @SerializedName(value = "PRIVATE", alternate = ["private"]) PRIVATE, - @SerializedName("all_users") + + @SerializedName("ALL_USERS", alternate = ["all_users"]) ALL_USERS } fun mapToDomain(): Account { return Account( username = username ?: "", - bio = bio?:"", + bio = bio ?: "", requiresParentalConsent = requiresParentalConsent ?: false, name = name ?: "", country = country ?: "", diff --git a/profile/src/main/java/org/openedx/profile/domain/model/Account.kt b/profile/src/main/java/org/openedx/profile/domain/model/Account.kt index f338fc452..ad2765040 100644 --- a/profile/src/main/java/org/openedx/profile/domain/model/Account.kt +++ b/profile/src/main/java/org/openedx/profile/domain/model/Account.kt @@ -35,9 +35,9 @@ data class Account( fun isLimited() = accountPrivacy == Privacy.PRIVATE - fun isOlderThanMinAge() : Boolean { + fun isOlderThanMinAge(): Boolean { val currentYear = Calendar.getInstance().get(Calendar.YEAR) - return yearOfBirth != null && currentYear - yearOfBirth > USER_MIN_YEAR + return !requiresParentalConsent && yearOfBirth != null && currentYear - yearOfBirth > USER_MIN_YEAR } } diff --git a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt index 46ed7f364..a51edfc0e 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt @@ -1,4 +1,7 @@ -@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class) +@file:OptIn( + ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class, + ExperimentalComposeUiApi::class +) package org.openedx.profile.presentation.edit @@ -40,6 +43,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.Card +import androidx.compose.material.ContentAlpha import androidx.compose.material.Divider import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon @@ -55,6 +59,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material.icons.outlined.Report import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material.rememberScaffoldState @@ -71,6 +76,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color @@ -177,6 +183,7 @@ class EditProfileFragment : Fragment() { val selectedImageUri by viewModel.selectedImageUri.observeAsState() val isImageDeleted by viewModel.deleteImage.observeAsState(false) val leaveDialog by viewModel.showLeaveDialog.observeAsState(false) + val focusManager = LocalFocusManager.current EditProfileScreen( windowSize = windowSize, @@ -212,6 +219,7 @@ class EditProfileFragment : Fragment() { } } } + focusManager.clearFocus() }, onDataChanged = { viewModel.profileDataChanged = it @@ -399,6 +407,8 @@ private fun EditProfileScreen( val isImeVisible by isImeVisibleState() + val requiresParentalConsent = uiState.account.requiresParentalConsent + LaunchedEffect(bottomSheetScaffoldState.isVisible) { if (!bottomSheetScaffoldState.isVisible) { focusManager.clearFocus() @@ -627,28 +637,32 @@ private fun EditProfileScreen( .clip(CircleShape) .noRippleClickable { - isOpenChangeImageDialogState = true - if (!uiState.account.isOlderThanMinAge()) { - openWarningMessageDialog = true + if (!requiresParentalConsent) { + isOpenChangeImageDialogState = true + if (!uiState.account.isOlderThanMinAge()) { + openWarningMessageDialog = true + } } } ) - Icon( - modifier = Modifier - .size(32.dp) - .clip(CircleShape) - .background(MaterialTheme.appColors.primary) - .padding(5.dp) - .clickable { - isOpenChangeImageDialogState = true - if (!uiState.account.isOlderThanMinAge()) { - openWarningMessageDialog = true - } - }, - painter = painterResource(id = R.drawable.profile_ic_edit_image), - contentDescription = null, - tint = MaterialTheme.appColors.onPrimary - ) + if (!requiresParentalConsent) { + Icon( + modifier = Modifier + .size(32.dp) + .clip(CircleShape) + .background(MaterialTheme.appColors.primary) + .padding(5.dp) + .clickable { + isOpenChangeImageDialogState = true + if (!uiState.account.isOlderThanMinAge()) { + openWarningMessageDialog = true + } + }, + painter = painterResource(id = R.drawable.profile_ic_edit_image), + contentDescription = null, + tint = MaterialTheme.appColors.onPrimary + ) + } } Spacer(modifier = Modifier.height(20.dp)) Text( @@ -657,12 +671,13 @@ private fun EditProfileScreen( style = MaterialTheme.appTypography.headlineSmall, color = MaterialTheme.appColors.textPrimary ) - Spacer(modifier = Modifier.height(24.dp)) - Text( - modifier = Modifier - .testTag("txt_edit_profile_limited_profile_label") - .clickable { - if (!LocaleUtils.isProfileLimited(mapFields[YEAR_OF_BIRTH].toString())) { + Spacer(modifier = Modifier.height(16.dp)) + if (!requiresParentalConsent) { + Text( + modifier = Modifier + .testTag("txt_edit_profile_limited_profile_label") + .clickable { +// if (!LocaleUtils.isProfileLimited(mapFields[YEAR_OF_BIRTH].toString())) { val privacy = if (uiState.isLimited) { Account.Privacy.ALL_USERS } else { @@ -670,14 +685,46 @@ private fun EditProfileScreen( } mapFields[ACCOUNT_PRIVACY] = privacy onLimitedProfileChange(!uiState.isLimited) +// } else { +// openWarningMessageDialog = true +// } + }, + text = stringResource(if (uiState.isLimited) R.string.profile_switch_to_full else R.string.profile_switch_to_limited), + color = MaterialTheme.appColors.textAccent, + style = MaterialTheme.appTypography.labelLarge + ) + Spacer(modifier = Modifier.height(16.dp)) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + if (requiresParentalConsent) { + Icon( + imageVector = Icons.Filled.VisibilityOff, + tint = MaterialTheme.appColors.textSecondary, + contentDescription = null + ) + Spacer(modifier = Modifier.width(4.dp)) + } + Text( + modifier = Modifier + .align(Alignment.CenterVertically) + .testTag("txt_edit_profile_limited_profile_message"), + text = stringResource( + if (requiresParentalConsent) { + R.string.profile_restricted_profile_message } else { - openWarningMessageDialog = true + R.string.profile_unrestricted_profile_message } - }, - text = stringResource(if (uiState.isLimited) R.string.profile_switch_to_full else R.string.profile_switch_to_limited), - color = MaterialTheme.appColors.textAccent, - style = MaterialTheme.appTypography.labelLarge - ) + ), + textAlign = TextAlign.Center, + color = MaterialTheme.appColors.textSecondary, + style = MaterialTheme.appTypography.bodySmall, + ) + } + Spacer(modifier = Modifier.height(20.dp)) ProfileFields( disabled = uiState.isLimited, @@ -895,43 +942,44 @@ private fun ProfileFields( LocaleUtils.getLanguageByLanguageCode(languageProficiency[0].code) } else "" Column(verticalArrangement = Arrangement.spacedBy(20.dp)) { +// SelectableField( +// name = stringResource(id = R.string.profile_year), +// initialValue = mapFields[YEAR_OF_BIRTH].toString(), +// onClick = { +// onFieldClick(YEAR_OF_BIRTH, context.getString(R.string.profile_year)) +// } +// ) SelectableField( - name = stringResource(id = R.string.profile_year), - initialValue = mapFields[YEAR_OF_BIRTH].toString(), + name = stringResource(id = R.string.profile_location), + initialValue = LocaleUtils.getCountryByCountryCode(mapFields[COUNTRY].toString()), + disabled = disabled, onClick = { - onFieldClick(YEAR_OF_BIRTH, context.getString(R.string.profile_year)) + onFieldClick(COUNTRY, context.getString(R.string.profile_location)) } ) - if (!disabled) { - SelectableField( - name = stringResource(id = R.string.profile_location), - initialValue = LocaleUtils.getCountryByCountryCode(mapFields[COUNTRY].toString()), - onClick = { - onFieldClick(COUNTRY, context.getString(R.string.profile_location)) - } - ) - SelectableField( - name = stringResource(id = R.string.profile_spoken_language), - initialValue = lang, - onClick = { - onFieldClick( - LANGUAGE, - context.getString(R.string.profile_spoken_language) - ) - } - ) - InputEditField( - modifier = Modifier - .fillMaxWidth() - .height(132.dp), - name = stringResource(id = R.string.profile_about_me), - initialValue = mapFields[BIO].toString(), - onValueChanged = { - onValueChanged(it.take(BIO_TEXT_FIELD_LIMIT)) - }, - onDoneClick = onDoneClick - ) - } + SelectableField( + name = stringResource(id = R.string.profile_spoken_language), + initialValue = lang, + disabled = disabled, + onClick = { + onFieldClick( + LANGUAGE, + context.getString(R.string.profile_spoken_language) + ) + } + ) + InputEditField( + modifier = Modifier + .fillMaxWidth() + .height(132.dp), + name = stringResource(id = R.string.profile_about_me), + initialValue = mapFields[BIO].toString(), + disabled = disabled, + onValueChanged = { + onValueChanged(it.take(BIO_TEXT_FIELD_LIMIT)) + }, + onDoneClick = onDoneClick + ) } } @@ -981,14 +1029,17 @@ private fun SelectableField( Icon( imageVector = Icons.Filled.ExpandMore, contentDescription = null, - tint = MaterialTheme.appColors.textPrimaryVariant + tint = MaterialTheme.appColors.textPrimaryVariant.copy( + alpha = if (disabled) 0.4f else 1f + ) ) }, modifier = Modifier .testTag("tf_select_${name.tagId()}") .fillMaxWidth() .noRippleClickable { - onClick() + if (!disabled) + onClick() }, placeholder = { Text( @@ -1040,7 +1091,9 @@ private fun InputEditField( shape = MaterialTheme.appShapes.textFieldShape, placeholder = { Text( - modifier = Modifier.testTag("txt_placeholder_${name.tagId()}"), + modifier = Modifier + .alpha(if (disabled) ContentAlpha.disabled else ContentAlpha.high) + .testTag("txt_placeholder_${name.tagId()}"), text = name, color = MaterialTheme.appColors.textFieldHint, style = MaterialTheme.appTypography.bodyMedium diff --git a/profile/src/main/res/values/strings.xml b/profile/src/main/res/values/strings.xml index 84cca0855..4b55dfd29 100644 --- a/profile/src/main/res/values/strings.xml +++ b/profile/src/main/res/values/strings.xml @@ -16,6 +16,8 @@ Spoken Language Switch to full profile Switch to limited profile + A limited profile only shares your username and profile photo. + Your profile information is only visible to you. Only your username is visible to others. Delete account Are you sure you want to delete your account?