package com.vpn.fastestvpnservice.screens import android.app.Activity import android.content.Context import android.content.Intent import android.content.res.Configuration import android.location.Location import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Warning import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material.rememberScaffoldState import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope 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.paint import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation 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.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.vpn.fastestvpnservice.R import com.vpn.fastestvpnservice.beans.Server import com.vpn.fastestvpnservice.beans.filterList import com.vpn.fastestvpnservice.beans.isDarkTheme import com.vpn.fastestvpnservice.constants.smartConnect import com.vpn.fastestvpnservice.helpers.BasePreferenceHelper import com.vpn.fastestvpnservice.sealedClass.Screen import com.vpn.fastestvpnservice.ui.theme.customTypography import com.vpn.fastestvpnservice.ui.theme.outfitFontFamily import com.vpn.fastestvpnservice.viewmodels.LoginViewModel import com.vpn.fastestvpnservice.viewmodels.SearchListViewModel import com.vpn.fastestvpnservice.viewmodels.ServerListViewModel import com.vpn.fastestvpnservice.viewmodels.SplashViewModel import com.vpn.fastestvpnservice.views.CustomValidation import com.vpn.fastestvpnservice.views.ShowCustomSnackBar import com.vpn.fastestvpnservice.widgets.SimpleAppWidget import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.random.Random @OptIn(ExperimentalComposeUiApi::class) @Composable fun Login(navHostController: NavHostController) { val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current val random = Random.nextInt(1,999) val loginViewModel: LoginViewModel = viewModel() val readOnly = loginViewModel.liveDataLoginStatus.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 showErrorPass1 by remember { mutableStateOf(false) } var showErrorPass2 by remember { mutableStateOf(false) } val context = LocalContext.current val basePreferenceHelper = BasePreferenceHelper(context) val snackBarState = remember { SnackbarHostState() } val snackBarStateRed = remember { SnackbarHostState() } Scaffold( content = { padding -> Box( modifier = Modifier .fillMaxSize() .paint( painter = painterResource(id = if (isDarkTheme.value) R.drawable.bg_app else R.drawable.bg_img3), contentScale = ContentScale.FillBounds ) .alpha(if (readOnly == true) 0.6F else 1F) .windowInsetsPadding(WindowInsets.systemBars) // .background( // if (isSystemInDarkTheme()) Color.Black // else Color.Transparent // ) .pointerInput(Unit) { detectTapGestures { focusManager.clearFocus() keyboardController?.hide() } }, ) { val view = LocalView.current val window = (view.context as Activity).window window.statusBarColor = Color.Transparent.toArgb() window.navigationBarColor = Color.Transparent.toArgb() ShowCustomSnackBar(snackBarState, R.color.switch_green, R.color.white) ShowCustomSnackBar(snackBarStateRed, R.color.Red, R.color.white) if (loginViewModel.liveDataLoginStatus.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 .size(50.dp) .align(Alignment.Center), color = colorResource(id = R.color.yellow_text), strokeWidth = 5.dp, ) } Image( painter = painterResource( id = R.drawable.fastestapp_logo3x), contentDescription = "FastestVPN", modifier = Modifier .padding(top = 50.dp) .size(width = 75.dp, height = 102.dp) .align(Alignment.TopCenter) , ) Column ( modifier = Modifier.align(Alignment.Center), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { TextMsg( text = context.getString(R.string.welcome_back), color = Color.White, style = MaterialTheme.typography.displayLarge ) Spacer(modifier = Modifier.height(8.dp)) TextMsg( text = context.getString(R.string.please_login), color = colorResource(id = R.color.white), style = MaterialTheme.typography.customTypography.labelLarge, alpha = 0.6F ) Spacer(modifier = Modifier.height(20.dp)) TextField( value = textChanged, onValueChange = { textChanged = it }, readOnly = readOnly!!, 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 = if (showErrorEmail) colorResource(id = R.color.red) else colorResource(id = R.color.white), shape = RoundedCornerShape(16.dp) ) .background(color = colorResource(id = R.color.transparent)) .clickable() { }, shape = RoundedCornerShape(16.dp), // placeholder = { // Text(text = "Enter email address", // color = colorResource(id = R.color.white)) // }, label = { Text(text = context.getString(R.string.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 = MaterialTheme.colorScheme.secondaryContainer, 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() } ), ) Spacer(modifier = Modifier.height(20.dp)) val color = if (showErrorPass1) { colorResource(id = R.color.red) } else if (showErrorPass2) { colorResource(id = R.color.red) } else { colorResource(id = R.color.white) } TextField( value = passwordChanged, onValueChange = { // Log.d("onClick_test", "onValueChange -> ") passwordChanged = it }, readOnly = readOnly, textStyle = MaterialTheme.typography.customTypography.bodyLarge, modifier = Modifier .padding(start = 15.dp, end = 15.dp) .align(Alignment.Start) .fillMaxWidth() .height(60.dp) .border( 1.dp, color = color, shape = RoundedCornerShape(16.dp) ) .background(color = colorResource(id = R.color.transparent)) .clickable() {}, shape = RoundedCornerShape(16.dp), // placeholder = { // Text(text = "Enter password", // color = colorResource(id = R.color.white)) // }, label = { Text(text = context.getString(R.string.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.transparent), focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, 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.Companion.None else PasswordVisualTransformation(), trailingIcon = { // val image // = if (passwordVisible) Icons.Filled.Visibility // else Icons.Filled.VisibilityOff val description = if (passwordVisible) "Hide Password" else "Show Password" IconButton(onClick = { if (!readOnly) { 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) ) } } } ) Spacer(modifier = Modifier.height(20.dp)) ClickableText( onClick = { if (!readOnly) { // Log.d("onClick_test", "Forgot Password Clicked") navHostController.navigate(Screen.ForgotPassword.route) } }, modifier = Modifier .padding(end = 15.dp) .align(Alignment.End), style = MaterialTheme.typography.customTypography.titleSmall, text = AnnotatedString(context.getString(R.string.forgot_pass)), ) } Column( modifier = Modifier.align(Alignment.BottomCenter) ) { SignInButton( navHostController = navHostController, textChanged, passwordChanged, loginViewModel = loginViewModel, basePreferenceHelper, showErrorEmail = { showErrorEmail = it }, showErrorEmail, showErrorPass = { showErrorPass1 = it }, showErrorPass1, showErrorPass2 = { showErrorPass2 = it }, showErrorPass2, snackBarState, snackBarStateRed, context ) Row ( modifier = Modifier .padding(bottom = 25.dp) .align(Alignment.CenterHorizontally), ){ TextMsgSignUp(navHostController = navHostController, loginViewModel, readOnly!!, context) } } } } ) } @Composable fun ColumnScope.ShowErrorRow( errorText: String ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .align(Alignment.Start) .padding(start = 16.dp, top = 3.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) ) } } @Composable fun RowScope.TextMsgSignUp( navHostController: NavHostController, loginViewModel: LoginViewModel, isEnabled: Boolean, context: Context ) { Text( modifier = Modifier .padding(0.dp), text = context.getString(R.string.dont_have_acc), style = MaterialTheme.typography.customTypography.labelLarge, ) ClickableText( onClick = { if (!isEnabled) { navHostController.navigate(Screen.SignUp.route) } }, modifier = Modifier .padding(0.dp), style = MaterialTheme.typography.customTypography.titleMedium, text = AnnotatedString(" ${context.getString(R.string.signup)}"), ) } @Composable fun ColumnScope.SignInButton( navHostController: NavHostController, email: String, password: String, loginViewModel: LoginViewModel, prefHelper: BasePreferenceHelper, showErrorEmail: (Boolean) -> Unit, isError: Boolean, showErrorPass: (Boolean) -> Unit, isErrorPass: Boolean, showErrorPass2: (Boolean) -> Unit, isErrorPass2: Boolean, snackBarState: SnackbarHostState, snackBarStateRed: SnackbarHostState, context: Context ) { val scaffoldState = rememberScaffoldState() val coroutineScope = rememberCoroutineScope() val customValidation = CustomValidation() if (isError) { if (email.isNotEmpty()) { showErrorEmail(false) } } if (isErrorPass) { if (password.isNotEmpty()) { showErrorPass(false) } } if (isErrorPass2) { if (password.isNotEmpty()) { val isErrorPassSize = customValidation.isValidPassword(password) if (isErrorPassSize) { showErrorPass2(false) } } } Button( modifier = Modifier .padding( start = 15.dp, end = 15.dp, bottom = 25.dp ) .background(colorResource(id = R.color.transparent)) .fillMaxWidth() .height(60.dp) .clickable() { }, onClick = { // Log.d("test_api_response live", "Login Clicked:") val isErrors = customValidation.isValidText(email, "Email") showErrorEmail(!isErrors) val isError1 = customValidation.isValidText(password, "Password") showErrorPass(!isError1) val isError2 = customValidation.isValidPassword(password) if (password.isNotEmpty()) { showErrorPass2(!isError2) } Log.d("test_snackbar", "sb in = $isErrors") if (!isErrors) { // ShowErrorRow(errorText = "Email is Empty") coroutineScope.launch { snackBarStateRed.showSnackbar("Email is Empty") } } else if (!isError1) { // ShowErrorRow(errorText = "Password is Empty") coroutineScope.launch { snackBarStateRed.showSnackbar("Password is Empty") } } else if (!isError2) { // ShowErrorRow(errorText = "Should be 3 or more!") coroutineScope.launch { snackBarStateRed.showSnackbar("Password should be 3 or more!") } } // showLoader = true if (loginViewModel.liveDataLoginStatus.value == false) { // Log.d("test_api_response live", "SignIn: $email $password") if (customValidation.isValidText(email, "Email") && customValidation.isValidText(password, "Password") && customValidation.isValidPassword(password)) { // Log.d("test_login_text", "true") if (email.isNotEmpty() && password.isNotEmpty()) { loginViewModel.setLoginStatus(true) loginViewModel.loginRequest( email, password, "android", "11", "3.2.4" ) } } } }, shape = RoundedCornerShape(15.dp), // border = BorderStroke(25.dp, colorResource(id = R.color.black)), colors = ButtonDefaults.buttonColors( contentColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.onSecondaryContainer, ), ) { Text(text = context.getString(R.string.signin), style = MaterialTheme.typography.titleMedium, textAlign = TextAlign.Center ) val context = LocalContext.current val loginData by loginViewModel.liveDataUserResponse.observeAsState() loginData?.let { response -> // Log.d("test_api_response live", "live: ${loginData?.status} ${loginData?.message}") loginViewModel.setLoginStatus(false) // showLoader = false if (response.status) { response.data?.let { prefHelper.setLoggedInState(true) prefHelper.savePassword(password) prefHelper.saveUser(it) it.wireguard?.let { wg -> prefHelper.saveWireGuard(wg) // Log.d("test_wg_data", "Login:: ${wg.ip} ${wg.key}") } 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]) prefHelper.saveFilterList(filterList[0]) prefHelper.saveSmartList(smartConnect[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: ", "Login: $it") } val smartLocationList: MutableList = ArrayList() prefHelper.getServerData().get(0).servers?.let { val serverDataLocation = it val distinctdatanotnull = serverDataLocation.filter { // servers's lt and lt not be null it.lt != null && it.lg != null } val distinctdata = distinctdatanotnull.distinctBy { // servers's lt filter, no same lt of a server it.lt } val result = FloatArray(1) val ipinfo = prefHelper.getIpinfo() distinctdata.forEachIndexed { index, server -> val lat1 = ipinfo?.latitute val lon1 = ipinfo?.longitude val lat2 = server.lt val lon2 = server.lg if (lat1 != null && lat2 != null && lon1 != null && lon2 != null) { Location.distanceBetween(lat1, lon1, lat2, lon2, result) } val distance: Float = result[0] distinctdata.get(index).distance = distance } val sortedDistance = distinctdata.sortedBy { it.distance } if (sortedDistance.isNotEmpty()) { for (i in 0..0) { smartLocationList.add(sortedDistance.get(i)) } } smartLocationList.forEach { // prefHelper.setSmartServerObject(it) prefHelper.setRecommendedServerObject(it) Log.d("smartLocationList", "L:: server = ${it.server_name}") } } // setCustomLocale(context) splashViewModelSplash = viewModel { SplashViewModel(context) } serverListViewModelSplash = viewModel { ServerListViewModel(context = context) } searchListViewModelSplash = viewModel { SearchListViewModel(context, serverListViewModelSplash, splashViewModelSplash) } serverListViewModelSplash.setRecommendedSmartServers() serverListViewModelSplash.setCountryData() val widgetIntent = Intent(context, SimpleAppWidget::class.java) widgetIntent.action = SimpleAppWidget.ACTION_LOGIN context.sendBroadcast(widgetIntent) navHostController.popBackStack() navHostController.navigate(Screen.BottomBarMainScreen.route) } } else { response.message?.let { // Log.d("test_login_msg", "${response.message}") coroutineScope.launch { snackBarStateRed.showSnackbar(it) } } } loginViewModel.mutableLiveDataUserResponse.value = null } } } @Composable fun ColumnScope.TextMsg( text: String, color: Color, style: TextStyle, alpha: Float = 1F ) { Text( modifier = Modifier .padding(start = 15.dp) .align(Alignment.Start) .alpha(alpha), style = style, text = text, color = color, ) } @Preview @Composable fun LoginPreview() { Login(rememberNavController()) } @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable fun LoginPreviewDark() { Login(rememberNavController()) }