| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 | package com.vpn.fastestvpnservice.screensimport android.content.res.Configurationimport android.util.Logimport android.widget.Toastimport androidx.compose.foundation.Imageimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.borderimport androidx.compose.foundation.gestures.detectTapGesturesimport androidx.compose.foundation.isSystemInDarkThemeimport androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.BoxScopeimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.ColumnScopeimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.Spacerimport androidx.compose.foundation.layout.fillMaxHeightimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.heightimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.layout.sizeimport androidx.compose.foundation.layout.widthimport androidx.compose.foundation.shape.RoundedCornerShapeimport androidx.compose.foundation.text.ClickableTextimport androidx.compose.foundation.text.KeyboardActionsimport androidx.compose.foundation.text.KeyboardOptionsimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Doneimport androidx.compose.material.icons.filled.DoneOutlineimport androidx.compose.material.icons.filled.Emailimport androidx.compose.material.icons.filled.Lockimport androidx.compose.material.icons.filled.Visibilityimport androidx.compose.material.icons.filled.VisibilityOffimport androidx.compose.material.icons.filled.Warningimport androidx.compose.material.icons.outlined.Visibilityimport androidx.compose.material.icons.sharp.Visibilityimport androidx.compose.material3.Buttonimport androidx.compose.material3.ButtonDefaultsimport androidx.compose.material3.CircularProgressIndicatorimport androidx.compose.material3.Iconimport androidx.compose.material3.IconButtonimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SnackbarHostStateimport androidx.compose.material3.Textimport androidx.compose.material3.TextFieldimport androidx.compose.material3.TextFieldDefaultsimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.livedata.observeAsStateimport androidx.compose.runtime.mutableFloatStateOfimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.ExperimentalComposeUiApiimport androidx.compose.ui.Modifierimport androidx.compose.ui.draw.alphaimport androidx.compose.ui.draw.paintimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.graphics.vector.ImageVectorimport androidx.compose.ui.input.pointer.pointerInputimport androidx.compose.ui.layout.ContentScaleimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.platform.LocalSoftwareKeyboardControllerimport androidx.compose.ui.res.colorResourceimport androidx.compose.ui.res.painterResourceimport androidx.compose.ui.res.vectorResourceimport androidx.compose.ui.text.AnnotatedStringimport androidx.compose.ui.text.TextStyleimport androidx.compose.ui.text.font.FontStyleimport androidx.compose.ui.text.font.FontWeightimport androidx.compose.ui.text.input.ImeActionimport androidx.compose.ui.text.input.KeyboardTypeimport androidx.compose.ui.text.input.PasswordVisualTransformationimport androidx.compose.ui.text.input.VisualTransformationimport androidx.compose.ui.text.style.LineHeightStyleimport androidx.compose.ui.text.style.TextAlignimport androidx.compose.ui.tooling.preview.Previewimport androidx.compose.ui.unit.dpimport androidx.compose.ui.unit.spimport androidx.lifecycle.viewmodel.compose.viewModelimport androidx.navigation.NavHostControllerimport androidx.navigation.compose.rememberNavControllerimport com.vpn.fastestvpnservice.Rimport com.vpn.fastestvpnservice.beans.themesListimport com.vpn.fastestvpnservice.helpers.BasePreferenceHelperimport com.vpn.fastestvpnservice.sealedClass.Screenimport com.vpn.fastestvpnservice.ui.theme.customTypographyimport com.vpn.fastestvpnservice.ui.theme.outfitFontFamilyimport com.vpn.fastestvpnservice.viewmodels.LoginViewModelimport com.vpn.fastestvpnservice.viewmodels.SignUpViewModelimport com.vpn.fastestvpnservice.views.CustomValidationimport com.vpn.fastestvpnservice.views.ShowCustomSnackBarimport kotlinx.coroutines.delayimport kotlinx.coroutines.launch@OptIn(ExperimentalComposeUiApi::class)@Composablefun SignUp(navHostController: NavHostController) {    val keyboardController = LocalSoftwareKeyboardController.current    val focusManager = LocalFocusManager.current    val signUpViewModel: SignUpViewModel = viewModel()    var isSignUpEnabled = signUpViewModel.liveDataSignUpStatus.observeAsState().value    var textChanged by remember { mutableStateOf("") }    var passwordChanged by remember { mutableStateOf("") }    var passwordVisible by remember { mutableStateOf(false) }    var showErrorEmail by remember { mutableStateOf(false) }    var showErrorEmail2 by remember { mutableStateOf(false) }    var showErrorPass1 by remember { mutableStateOf(false) }    var showErrorPass2 by remember { mutableStateOf(false) }    val snackBarState = remember { SnackbarHostState() }    Scaffold(        content = { padding ->            Box(                modifier = Modifier                    .paint(                        painter = painterResource(id = R.drawable.bg_img3),                        contentScale = ContentScale.FillBounds                    )                    .alpha(if (isSignUpEnabled!!) 0.6F else 1F)//            .background(//                if (isSystemInDarkTheme()) Color.Black//                else Color.Transparent//            )                    .fillMaxSize()                    .pointerInput(Unit) {                        detectTapGestures {                            focusManager.clearFocus()                            keyboardController?.hide()                        }                    },            ) {                ShowCustomSnackBar(snackBarState)                Column (                    modifier = Modifier                        .fillMaxHeight()                        .fillMaxWidth()                    ,                    verticalArrangement = Arrangement.Top,                    horizontalAlignment = Alignment.CenterHorizontally                ) {                    Spacer(modifier = Modifier.height(50.dp))                    IconButton(                        onClick = {                            if (!isSignUpEnabled) {                                navHostController.popBackStack()                            }                        },                        modifier = Modifier                            .padding(start = 16.dp, top = 25.dp)                            .align(Alignment.Start)                    )                    {                        Icon(painter = painterResource(                            id = R.drawable.back_arrow3x),                            contentDescription = "Back Button",                            tint = colorResource(id = R.color.white),                            modifier = Modifier                                .height(36.dp)                                .width(36.dp)                                .align(Alignment.Start),                        )                    }                    Spacer(modifier = Modifier.height(44.dp))                    Image(                        painter = painterResource(id = R.drawable.fastestapp_logo3x),                        contentDescription = "FastestVPN",                        modifier = Modifier                            .size(75.dp, 102.dp)                            .background(Color.Transparent)                            .padding(top = 0.dp),                    )                    Spacer(modifier = Modifier.height(50.dp))                    Text(                        modifier = Modifier                            .padding(start = 15.dp)                            .align(Alignment.Start)                        ,                        style = MaterialTheme.typography.displayLarge,                        text = "Hello There!",                        color = Color.White,                    )                    Text(                        modifier = Modifier                            .padding(start = 15.dp)                            .align(Alignment.Start)                            .alpha(0.6F),                        style = MaterialTheme.typography.customTypography.labelLarge,                        text = "Please register your account.",                        color = colorResource(id = R.color.white),                    )                    Spacer(modifier = Modifier.height(20.dp))                    val colorEmail = if (showErrorEmail || showErrorEmail2) {                        colorResource(id = R.color.red)                    } else {                        colorResource(id = R.color.white)                    }                    TextField(                        value = textChanged,                        onValueChange = {                            textChanged = it                        },                        readOnly = isSignUpEnabled,                        textStyle = MaterialTheme.typography.customTypography.bodyMedium,                        modifier = Modifier                            .padding(start = 15.dp, end = 15.dp, top = 10.dp)                            .align(Alignment.Start)                            .fillMaxWidth()                            .height(60.dp)                            .border(                                1.dp,                                color = colorEmail,                                shape = RoundedCornerShape(16.dp)                            ),//                placeholder = {//                    Text(text = "Enter email address",//                        color = colorResource(id = R.color.white),//                        fontSize = 14.sp,//                    )//                },                        label = {                            Text(text = "Email",                                style = MaterialTheme.typography.customTypography.bodySmall                            )                        },                        leadingIcon = {                            Icon(                                painter = painterResource(id = R.drawable.sms3x),                                contentDescription = "Email Logo",                                tint = colorResource(id = R.color.white),                                modifier = Modifier                                    .size(24.dp, 24.dp)                            )                        },                        maxLines = 1,                        colors = TextFieldDefaults.colors(                            focusedLabelColor = Color.Blue,                            unfocusedContainerColor = colorResource(id = R.color.transparent),                            focusedContainerColor = colorResource(id = R.color.transparent),                            focusedIndicatorColor = colorResource(id = R.color.transparent),                            disabledIndicatorColor = colorResource(id = R.color.transparent),                            unfocusedIndicatorColor = colorResource(id = R.color.transparent),                            cursorColor = colorResource(id = R.color.white)                        ),                        keyboardOptions = KeyboardOptions(                            keyboardType = KeyboardType.Email,                            imeAction = ImeAction.Done                        ),                        keyboardActions = KeyboardActions(                            onDone = {                                focusManager.clearFocus()                                keyboardController?.hide()                            }                        ),                    )                    if (showErrorEmail) {                        ShowErrorRow(errorText = "Email is Empty")                    }                    else if (showErrorEmail2) {                        ShowErrorRow(errorText = "Email format incorrect")                    }                    Spacer(modifier = Modifier.height(20.dp))                    val colorPass = if (showErrorPass1 || showErrorPass2) {                        colorResource(id = R.color.red)                    } else {                        colorResource(id = R.color.grey_password_field)                    }                    TextField(                        value = passwordChanged,                        onValueChange = {                            Log.d("onClick_test", "onValueChange -> ")                            passwordChanged = it                        },                        readOnly = isSignUpEnabled,                        textStyle = MaterialTheme.typography.customTypography.bodyMedium,                        modifier = Modifier                            .padding(start = 15.dp, end = 15.dp)                            .align(Alignment.Start)                            .fillMaxWidth()                            .height(60.dp)                            .border(                                1.dp,                                color = colorPass,                                shape = RoundedCornerShape(16.dp)                            )                            .background(color = colorResource(id = R.color.transparent)),                        shape = RoundedCornerShape(15.dp),//                placeholder = {//                    Text(text = "Enter password",//                        color = colorResource(id = R.color.white))//                },                        label = {                            Text(text = "Password",                                style = MaterialTheme.typography.customTypography.bodyLarge                            )                        },                        leadingIcon = {                            Icon(                                painter = painterResource(id = R.drawable.lock3x),                                contentDescription = "Password Logo",                                tint = colorResource(id = R.color.white),                                modifier = Modifier                                    .size(24.dp, 24.dp)                            )                        },                        maxLines = 1,                        colors = TextFieldDefaults.colors(                            focusedLabelColor = Color.Blue,                            unfocusedContainerColor = colorResource(id = R.color.grey_password_field),                            focusedContainerColor = colorResource(id = R.color.grey_password_field),                            focusedIndicatorColor = colorResource(id = R.color.transparent),                            disabledIndicatorColor = colorResource(id = R.color.transparent),                            unfocusedIndicatorColor = colorResource(id = R.color.transparent),                            cursorColor = colorResource(id = R.color.white)                        ),                        keyboardOptions = KeyboardOptions(                            keyboardType = KeyboardType.Password,                            imeAction = ImeAction.Done                        ),                        keyboardActions = KeyboardActions(                            onDone = {                                focusManager.clearFocus()                                keyboardController?.hide()                            }                        ),                        visualTransformation =                        if (passwordVisible) VisualTransformation.None                        else PasswordVisualTransformation(),                        trailingIcon = {                            Log.d("onClick_test", "trailingIcon -> ")//                    val image//                            = if (passwordVisible) Icons.Filled.Visibility//                    else Icons.Filled.VisibilityOff                            val description = if (passwordVisible) "Hide Password"                            else "Show Password"                            IconButton(onClick = {                                if (!isSignUpEnabled) {                                    passwordVisible = !passwordVisible                                }                            })                            {                                if (passwordVisible) {                                    Icon(                                        imageVector = Icons.Outlined.Visibility,                                        contentDescription = description,                                        tint = colorResource(id = R.color.white),                                        modifier = Modifier.size(24.dp)                                    )                                }                                else {                                    Icon(                                        painter = painterResource(id = R.drawable.eye_slash3x),                                        contentDescription = description,                                        tint = colorResource(id = R.color.white),                                        modifier = Modifier.size(24.dp)                                    )                                }                            }                        }                    )                    if (showErrorPass1) {                        ShowErrorRow(errorText = "Password is Empty")                    }                    else if (showErrorPass2) {                        ShowErrorRow(errorText = "Should be 3 or more!")                    }                    Spacer(modifier = Modifier.height(15.dp))                    Row (                        modifier = Modifier                            .padding(start = 15.dp, top = 16.dp)                            .fillMaxWidth(),                    ){                        val colorLogo = if (showErrorEmail || showErrorEmail2 || showErrorPass1 || showErrorPass2) colorResource(                            id = R.color.red                         ) else colorResource(id = R.color.white)                        Icon(                            painter = painterResource(id = R.drawable.tick_square3x),                            contentDescription = "Logo",                            tint = colorLogo,                            modifier = Modifier.size(24.dp, 24.dp)                        )                        Text(                            modifier = Modifier                                .padding(start = 15.dp),                            style = MaterialTheme.typography.customTypography.bodyLarge,                            text = "By creating your account, you agree to",                            color = colorResource(id = R.color.white),                        )                    }                    Spacer(modifier = Modifier.height(0.dp))                    Row (                        modifier = Modifier                            .padding(start = 15.dp)                            .fillMaxWidth(),                    ){                        Icon(                            painter = painterResource(id = R.drawable.tick_square3x),                            contentDescription = "Logo",                            tint = colorResource(id = R.color.transparent),                            modifier = Modifier.size(24.dp),                        )                        ClickableText(                            onClick = {                                if (!isSignUpEnabled) {                                    navHostController.navigate(                                        Screen.TermsAndConditions.route                                    )                                }                            },                            modifier = Modifier                                .padding(start = 15.dp),                            text = AnnotatedString("Terms & Conditions"),                            style = MaterialTheme.typography.customTypography.titleLarge                        )                    }                }                SignUpButton(                    signUpViewModel,                    textChanged,                    passwordChanged,                    navHostController,                    showErrorEmail1 = {                        showErrorEmail = it                    },                    showErrorEmail,                    showErrorPass1 = {                        showErrorPass1 = it                    },                    showErrorPass1,                    showErrorPass2 = {                        showErrorPass2 = it                    },                    showErrorPass2,                    showErrorEmail2 = {                        showErrorEmail2 = it                    },                    showErrorEmail2,                    snackBarState                )                Row (                    modifier = Modifier                        .align(Alignment.BottomCenter)                        .padding(40.dp),                ){                    Text(                        modifier = Modifier                            .padding(0.dp),                        style = MaterialTheme.typography.customTypography.labelLarge,                        text = "Already have an account? ",                        color = Color.White,                    )                    ClickableText(                        onClick = {                            if (!isSignUpEnabled) {                                navHostController.popBackStack()                                if (navHostController.currentDestination?.id != null &&                                    navHostController.currentDestination?.route != null){                                    Log.d("test_signup", "From Sign In -> id = " + navHostController.currentDestination?.id                                            + " route = " + navHostController.currentDestination?.route                                    )                                }                                else {                                    Log.d("test_signup", "From Sign Up -> id = " + navHostController.currentDestination?.id                                            + " route = " + navHostController.currentDestination?.route                                    )                                    navHostController.navigate(Screen.Login.route)                                }                            }                        },                        modifier = Modifier                            .padding(0.dp),                        style = MaterialTheme.typography.customTypography.titleMedium,                        text = AnnotatedString(" Sign In"),                    )                }            }        }    )}@Composablefun ColumnScope.ShowErrorRowSignUp(    errorText: String) {    Row(        verticalAlignment = Alignment.CenterVertically,        modifier = Modifier            .align(Alignment.Start)            .padding(start = 16.dp, top = 8.dp)    ) {        Icon(imageVector = Icons.Default.Warning,            contentDescription = "Error",            tint = colorResource(id = R.color.red),            modifier = Modifier.size(14.dp)        )        Text(text = errorText,            style = TextStyle(                fontFamily = outfitFontFamily,                fontWeight = FontWeight.Normal,                fontSize = 14.sp,                color = colorResource(id = R.color.red)            ),            modifier = Modifier.padding(start = 5.dp)        )    }}@Composablefun BoxScope.SignUpButton(    signUpViewModel: SignUpViewModel,    email: String,    password: String,    navHostController: NavHostController,    showErrorEmail1: (Boolean) -> Unit,    isErrorEmail1: Boolean,    showErrorPass1: (Boolean) -> Unit,    isErrorPass1: Boolean,    showErrorPass2: (Boolean) -> Unit,    isErrorPass2: Boolean,    showErrorEmail2: (Boolean) -> Unit,    isErrorEmail2: Boolean,    snackBarState: SnackbarHostState) {    val loginViewModel: LoginViewModel = viewModel()    val context = LocalContext.current    val prefHelper = BasePreferenceHelper(context)    val customValidation = CustomValidation()    val coroutineScope = rememberCoroutineScope()    if (isErrorEmail1) {        if (email.isNotEmpty()) {            showErrorEmail1(false)        }    }    if (isErrorEmail2) {        if (email.isNotEmpty()) {            val emailFormat = customValidation.isValidEmail(email)            if (emailFormat) {                showErrorEmail2(false)            }        }    }    if (isErrorPass1) {        if (password.isNotEmpty()) {            showErrorPass1(false)        }    }    if (isErrorPass2) {        if (password.isNotEmpty()) {            val isErrorPassSize = customValidation.isValidPassword(password)            if (isErrorPassSize) {                showErrorPass2(false)            }        }    }    if (signUpViewModel.liveDataSignUpStatus.value == true) {        var progress by remember { mutableFloatStateOf(0.1F) }            LaunchedEffect(key1 = Unit) {                while (true) {                    for (i in 1..100) {                        progress = i.toFloat()/100F                        delay(150)                    }                    progress = 0.1F                }            }        CircularProgressIndicator(            progress = { progress },            modifier = Modifier                .align(Alignment.Center)                .size(50.dp),            color = colorResource(id = R.color.yellow_text),            strokeWidth = 5.dp,        )    }    Button(        onClick = {//            navHostController.popBackStack()//            navHostController.navigate(Screen.BottomBarMainScreen.route)            val isErrors = customValidation.isValidText(email, "Email")            showErrorEmail1(!isErrors)            val isErrors4 = customValidation.isValidEmail(email)            if (email.isNotEmpty()) {                showErrorEmail2(!isErrors4)            }            val isErrors2 = customValidation.isValidText(password, "Password")            showErrorPass1(!isErrors2)            val isError3 = customValidation.isValidPassword(password)            if (password.isNotEmpty()) {                showErrorPass2(!isError3)            }            if (signUpViewModel.liveDataSignUpStatus.value == false) {                Log.d("test_button", "onClick")                if (customValidation.isValidText(email, "Email") &&                    customValidation.isValidText(password, "Password") &&                    customValidation.isValidPassword(password)) {                    if (customValidation.isValidEmail(email)) {                        signUpViewModel.setSignUpStatus(true)                        signUpViewModel.signUp(email, password)                    }                }            }        },        modifier = Modifier            .padding(                start = 15.dp, end = 15.dp,                bottom = 100.dp            )            .align(Alignment.BottomCenter)            .background(colorResource(id = R.color.transparent))            .fillMaxWidth()            .height(60.dp),        shape = RoundedCornerShape(15.dp),//            border = BorderStroke(25.dp, colorResource(id = R.color.black)),        colors = ButtonDefaults.buttonColors(            contentColor = colorResource(id = R.color.light_blue),            containerColor = colorResource(id = R.color.white),        ),    )    {        Text(text = "Sign Up",            style = MaterialTheme.typography.titleMedium,            textAlign = TextAlign.Center        )        val signUpData = signUpViewModel.liveDataSignUp.observeAsState().value        signUpData?.let { response ->            signUpViewModel.setSignUpStatus(false)            if (response.status) {                signUpViewModel.setSignUpStatus(true)                loginViewModel.loginRequest(                    email, password, "android", "11", "3.2.4"                )            } else {               response.message?.let {                   coroutineScope.launch {                       snackBarState.showSnackbar(it)                   }               }//                Toast.makeText(context, response.message, Toast.LENGTH_SHORT).show()            }            signUpViewModel.mutableLiveData.value = null        }        val loginData by loginViewModel.liveDataUserResponse.observeAsState()        loginData?.let { response ->            Log.d("test_api_response live", "live: ${loginData?.status} ${loginData?.message}")            signUpViewModel.setSignUpStatus(false)            if (response.status) {                response.data?.let {                    prefHelper.setLoggedInState(true)                    prefHelper.savePassword(password)                    prefHelper.saveUser(it)                    it.wireguard?.let { it1 -> prefHelper.saveWireGuard(it1) }                    it.product?.let { it1 -> prefHelper.saveProduct(it1) }                    prefHelper.saveEnabledProtocols(it.enabled_protocols)                    prefHelper.saveAvailableProtocols(it.available_protocols)                    prefHelper.saveXPlatformToken(it.userinfo?.email + "_" + System.currentTimeMillis())                    prefHelper.saveAdBlockState(false)                    prefHelper.saveTheme(themesList[0])                    it.servers?.let {                        prefHelper.saveServerData(it)                    }                    Log.d("bearer_token", it.token.toString())//                    upgradePriceViewModel.getProducts()                    prefHelper.getFcmToken().let {                        loginViewModel.sendFcmToken(it)                        Log.d("Refreshed token: ","SignUp: $it")                    }                }                navHostController.popBackStack()                navHostController.navigate(Screen.BottomBarMainScreen.route)            }            loginViewModel.mutableLiveDataUserResponse.value = null        }    }}@Preview@Composablefun SignUpPreview() {    SignUp(rememberNavController())}@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)@Composablefun SignUpPreviewDark() {    SignUp(rememberNavController())}
 |