123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- package ru.mephi.voip.ui.profile
- import android.os.Bundle
- import android.view.LayoutInflater
- import android.view.View
- import android.view.ViewGroup
- import android.widget.Toast
- import androidx.compose.foundation.Image
- import androidx.compose.foundation.background
- import androidx.compose.foundation.layout.*
- import androidx.compose.foundation.lazy.LazyColumn
- import androidx.compose.foundation.lazy.items
- import androidx.compose.foundation.shape.CircleShape
- import androidx.compose.foundation.text.KeyboardActions
- import androidx.compose.foundation.text.KeyboardOptions
- import androidx.compose.material.*
- import androidx.compose.material.icons.Icons
- import androidx.compose.material.icons.filled.Add
- import androidx.compose.material.icons.filled.CheckCircle
- import androidx.compose.material.icons.filled.Delete
- import androidx.compose.material.icons.filled.Edit
- import androidx.compose.runtime.*
- import androidx.compose.runtime.livedata.observeAsState
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.ExperimentalComposeUiApi
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.draw.clip
- import androidx.compose.ui.focus.FocusRequester
- import androidx.compose.ui.focus.focusRequester
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.graphics.RectangleShape
- import androidx.compose.ui.layout.ContentScale
- import androidx.compose.ui.platform.ComposeView
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.platform.LocalSoftwareKeyboardController
- import androidx.compose.ui.res.colorResource
- import androidx.compose.ui.res.dimensionResource
- import androidx.compose.ui.res.painterResource
- import androidx.compose.ui.res.stringResource
- import androidx.compose.ui.text.SpanStyle
- import androidx.compose.ui.text.buildAnnotatedString
- 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.style.TextAlign
- import androidx.compose.ui.text.withStyle
- import androidx.compose.ui.unit.dp
- import androidx.compose.ui.unit.sp
- import androidx.fragment.app.Fragment
- import androidx.navigation.NavController
- import androidx.navigation.fragment.findNavController
- import coil.annotation.ExperimentalCoilApi
- import coil.compose.ImagePainter
- import coil.compose.rememberImagePainter
- import kotlinx.coroutines.launch
- import org.koin.android.ext.android.inject
- import ru.mephi.voip.R
- import ru.mephi.voip.call.abto.AccountStatus
- import ru.mephi.voip.data.model.Account
- import ru.mephi.voip.data.model.NameItem
- import ru.mephi.voip.ui.MainActivity
- @ExperimentalComposeUiApi
- @ExperimentalCoilApi
- @ExperimentalMaterialApi
- class ProfileFragment : Fragment() {
- private val viewModel: ProfileViewModel by inject()
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- ProfileScreen(findNavController())
- }
- }
- }
- //https://dev.to/davidibrahim/how-to-use-multiple-bottom-sheets-in-android-compose-382p
- @ExperimentalMaterialApi
- @ExperimentalComposeUiApi
- @ExperimentalCoilApi
- @Composable
- fun ProfileScreen(navController: NavController) {
- val scope = rememberCoroutineScope()
- val scaffoldState = rememberBottomSheetScaffoldState()
- var currentBottomSheet: BottomSheetScreen? by remember {
- mutableStateOf(null)
- }
- if (scaffoldState.bottomSheetState.isCollapsed)
- currentBottomSheet = null
- // to set the current sheet to null when the bottom sheet closes
- if (scaffoldState.bottomSheetState.isCollapsed)
- currentBottomSheet = null
- val closeSheet: () -> Unit = {
- scope.launch {
- scaffoldState.bottomSheetState.collapse()
- }
- }
- val openSheet: (BottomSheetScreen) -> Unit = {
- scope.launch {
- currentBottomSheet = it
- scaffoldState.bottomSheetState.expand()
- }
- }
- BottomSheetScaffold(
- sheetElevation = 20.dp,
- sheetPeekHeight = 0.dp, scaffoldState = scaffoldState,
- sheetShape = BottomSheetShape,
- sheetContent = {
- currentBottomSheet?.let { currentSheet ->
- SheetLayout(currentSheet, closeSheet, scaffoldState)
- }
- }) { paddingValues ->
- Box(Modifier.padding(paddingValues)) {
- MainContent(openSheet, navController)
- }
- }
- }
- @ExperimentalMaterialApi
- @ExperimentalComposeUiApi
- @Composable
- fun SheetLayout(
- currentScreen: BottomSheetScreen,
- onCloseBottomSheet: () -> Unit,
- scaffoldState: BottomSheetScaffoldState
- ) {
- BottomSheetWithCloseDialog(
- onCloseBottomSheet, title =
- when (currentScreen) {
- BottomSheetScreen.ScreenAddNewAccount -> stringResource(id = R.string.add_new_account)
- BottomSheetScreen.ScreenChangeAccount -> stringResource(id = R.string.change_account)
- else -> ""
- }
- ) {
- when (currentScreen) {
- BottomSheetScreen.ScreenAddNewAccount -> ScreenAddNewAccount(scaffoldState)
- BottomSheetScreen.ScreenChangeAccount -> ScreenChangeAccount()
- }
- }
- }
- @ExperimentalMaterialApi
- @ExperimentalComposeUiApi
- @Composable
- fun ScreenAddNewAccount(scaffoldState: BottomSheetScaffoldState) {
- var textLogin = viewModel.newLogin.value
- var textPassword = viewModel.newPassword.value
- val context = LocalContext.current
- val maxNumberLength = 6
- val (focusRequester) = FocusRequester.createRefs()
- val keyboardController = LocalSoftwareKeyboardController.current
- val scope = rememberCoroutineScope()
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .background(Color.White, shape = RectangleShape)
- .height(400.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- OutlinedTextField(
- singleLine = true,
- keyboardOptions = KeyboardOptions(
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Next
- ),
- keyboardActions = KeyboardActions(
- onNext = { focusRequester.requestFocus() }
- ),
- colors = TextFieldDefaults.outlinedTextFieldColors(
- focusedBorderColor = colorResource(id = R.color.colorPrimaryDark),
- focusedLabelColor = colorResource(id = R.color.colorAccent),
- cursorColor = colorResource(id = R.color.colorAccent),
- ),
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- label = { Text("SIP USER ID") },
- value = textLogin,
- onValueChange = {
- if (it.length <= maxNumberLength) {
- textLogin = it
- viewModel.onNewAccountInputChange(login = it)
- } else
- Toast.makeText(
- context,
- "Номер не может быть больше $maxNumberLength символов",
- Toast.LENGTH_SHORT
- ).show()
- }
- )
- OutlinedTextField(
- singleLine = true,
- colors = TextFieldDefaults.outlinedTextFieldColors(
- focusedBorderColor = colorResource(id = R.color.colorPrimaryDark),
- focusedLabelColor = colorResource(id = R.color.colorAccent),
- cursorColor = colorResource(id = R.color.colorAccent),
- ),
- enabled = false,
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- label = { Text("SIP Server") },
- value = stringResource(id = R.string.domain),
- onValueChange = { }
- )
- OutlinedTextField(
- singleLine = true,
- colors = TextFieldDefaults.outlinedTextFieldColors(
- focusedBorderColor = colorResource(id = R.color.colorPrimaryDark),
- focusedLabelColor = colorResource(id = R.color.colorAccent),
- cursorColor = colorResource(id = R.color.colorAccent),
- ),
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
- keyboardActions = KeyboardActions(
- onDone = { keyboardController?.hide() }
- ),
- visualTransformation = PasswordVisualTransformation(),
- modifier = Modifier
- .focusRequester(focusRequester)
- .fillMaxWidth()
- .padding(16.dp),
- label = { Text("SIP PASSWORD") },
- value = textPassword,
- onValueChange = {
- viewModel.onNewAccountInputChange(password = it)
- }
- )
- OutlinedButton(onClick = {
- if (textLogin.toIntOrNull() == null || textPassword.isEmpty()) {
- Toast.makeText(context, "Введены некоректные данные", Toast.LENGTH_SHORT).show()
- } else if (viewModel.getAllAccounts().map { it.login }.contains(textLogin)) {
- Toast.makeText(context, "Такой аккаунт уже существует", Toast.LENGTH_SHORT)
- .show()
- } else {
- viewModel.addNewAccount()
- scope.launch {
- scaffoldState.bottomSheetState.collapse()
- }
- }
- viewModel.newLogin.value = ""
- viewModel.newPassword.value = ""
- }) {
- Text("Добавить", color = colorResource(id = R.color.colorPrimaryDark))
- }
- }
- }
- @Composable
- fun ScreenChangeAccount() {
- val mList: MutableList<Account> by remember { mutableStateOf(viewModel.getAllAccounts()) }
- viewModel.accountList.observe(viewLifecycleOwner) {
- mList.apply {
- clear()
- addAll(viewModel.getAllAccounts())
- }
- }
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .height(200.dp)
- .background(Color.White, shape = RectangleShape)
- ) {
- LazyColumn {
- items(items = mList) { acc ->
- AccountItem(acc)
- }
- }
- }
- }
- @Composable
- fun AccountItem(account: Account) {
- val context = LocalContext.current
- Card(
- shape = MaterialTheme.shapes.medium, elevation = 2.dp,
- modifier = Modifier
- .padding(vertical = 4.dp, horizontal = 20.dp)
- .fillMaxWidth()
- .height(50.dp)
- ) {
- Box {
- IconButton(
- onClick = {
- if ((context as MainActivity).hasPermissions()) {
- viewModel.updateActiveAccount(account)
- // Toast.makeText(
- // context,
- // "Активный аккаунт: ${viewModel.newLogin.value}",
- // Toast.LENGTH_SHORT
- // ).show()
- } else
- context.requestPermissions()
- // viewModel.clear()
- // mList.addAll(viewModel.getAllAccounts())
- }, modifier = Modifier
- .align(Alignment.CenterStart)
- .padding(10.dp)
- ) {
- Icon(
- Icons.Filled.CheckCircle, "Status",
- tint = if (account.isActive)
- colorResource(id = R.color.colorAccent)
- else
- colorResource(id = R.color.colorGray),
- )
- }
- Text(
- text = account.login,
- modifier = Modifier
- .padding(6.dp)
- .align(Alignment.Center),
- fontSize = 20.sp,
- fontWeight = FontWeight.Medium,
- )
- IconButton(
- onClick = {
- viewModel.removeAccount(account)
- }, modifier = Modifier
- .align(Alignment.CenterEnd)
- .padding(10.dp)
- ) {
- Icon(
- Icons.Filled.Delete, "Delete",
- tint = Color.Red
- )
- }
- }
- }
- }
- sealed class BottomSheetScreen {
- object ScreenAddNewAccount : BottomSheetScreen()
- object ScreenChangeAccount : BottomSheetScreen()
- }
- @ExperimentalCoilApi
- @Composable
- fun MainContent(
- openSheet: (BottomSheetScreen) -> Unit,
- navController: NavController
- ) {
- val painter = rememberImagePainter(
- data = viewModel.getImageUrl(),
- builder = {
- crossfade(true)
- }
- )
- val name = viewModel.displayName.observeAsState(NameItem("", ""))
- val accountStatus = viewModel.status.observeAsState(AccountStatus.UNREGISTERED)
- Column(
- Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(15.dp)
- ) {
- Text(
- modifier = Modifier.align(alignment = Alignment.TopCenter),
- text = stringResource(R.string.sip_account_header),
- fontSize = dimensionResource(id = R.dimen.toolbar_headers).value.sp,
- )
- IconButton(
- modifier = Modifier.align(alignment = Alignment.TopEnd),
- onClick = {
- navController.navigate(
- R.id.action_navigation_profile_to_settingsFragment,
- )
- }) {
- Icon(
- painter = painterResource(id = R.drawable.ic_baseline_settings_24),
- contentDescription = "Настройки"
- )
- }
- }
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Image(
- painter = painterResource(id = R.drawable.logo_mephi),
- contentDescription = "лого"
- )
- Box(modifier = Modifier.size(100.dp)) {
- Image(
- painter = painter,
- contentDescription = "",
- contentScale = ContentScale.Crop,
- modifier = Modifier
- .clip(CircleShape)
- .fillMaxSize()
- .align(Alignment.BottomEnd)
- )
- Icon(
- Icons.Filled.CheckCircle, "Статус",
- tint = when (accountStatus.value) {
- AccountStatus.REGISTERED -> colorResource(id = R.color.colorGreen)
- AccountStatus.UNREGISTERED, AccountStatus.REGISTRATION_FAILED -> Color.Red
- AccountStatus.NO_CONNECTION, AccountStatus.CHANGING, AccountStatus.LOADING -> Color.Gray
- },
- modifier = Modifier
- .align(Alignment.BottomEnd)
- )
- when (painter.state) {
- is ImagePainter.State.Loading -> {
- CircularProgressIndicator(Modifier.align(Alignment.Center))
- }
- is ImagePainter.State.Error -> {
- // If you wish to display some content if the request fails
- }
- }
- }
- if (accountStatus.value == AccountStatus.REGISTRATION_FAILED
- || accountStatus.value == AccountStatus.UNREGISTERED
- )
- IconButton(
- modifier = Modifier
- .align(Alignment.Bottom),
- onClick = {
- viewModel.retryRegistration()
- }
- ) {
- Icon(
- painter = painterResource(
- id = R.drawable.ic_baseline_update_24
- ),
- contentDescription = "Обновить",
- tint = Color.Red
- )
- }
- }
- Column(
- Modifier
- .fillMaxWidth()
- .padding(20.dp),
- horizontalAlignment = Alignment.Start
- ) {
- if (!name.value?.display_name.isNullOrEmpty())
- Text(
- fontSize = 25.sp,
- fontWeight = FontWeight.Medium,
- textAlign = TextAlign.Left,
- text = buildAnnotatedString {
- withStyle(style = SpanStyle(color = colorResource(id = R.color.colorAccent))) {
- append("Имя: ")
- }
- append(name.value!!.display_name)
- }
- )
- viewModel.getUserNumber()?.let {
- Text(
- fontSize = 25.sp,
- fontWeight = FontWeight.Medium,
- textAlign = TextAlign.Left,
- text = buildAnnotatedString {
- withStyle(style = SpanStyle(color = colorResource(id = R.color.colorAccent))) {
- append("Номер SIP: ")
- }
- append(it)
- }
- )
- }
- Text(
- fontSize = 25.sp,
- fontWeight = FontWeight.Medium,
- textAlign = TextAlign.Left,
- text = buildAnnotatedString {
- withStyle(style = SpanStyle(color = colorResource(id = R.color.colorAccent))) {
- append("Статус: ")
- }
- append(accountStatus.value.status)
- }
- )
- }
- Column(
- modifier = Modifier
- .fillMaxSize(), verticalArrangement = Arrangement.Bottom
- ) {
- ExtendedFloatingActionButton(
- icon = { Icon(Icons.Filled.Edit, "", tint = Color.White) },
- text = {
- Text(
- text = stringResource(R.string.change_account) + " (${viewModel.getAllAccounts().size})",
- color = Color.White
- )
- },
- modifier = Modifier
- .align(Alignment.End)
- .padding(16.dp, 16.dp, 16.dp, 0.dp),
- backgroundColor = colorResource(id = R.color.colorGreen),
- onClick = {
- openSheet(BottomSheetScreen.ScreenChangeAccount)
- // navController.navigate(
- // R.id.action_navigation_profile_to_settingsFragment,
- // )
- }
- )
- ExtendedFloatingActionButton(
- icon = { Icon(Icons.Filled.Add, "", tint = Color.White) },
- text = {
- Text(
- text = stringResource(R.string.add_new_account),
- color = Color.White
- )
- },
- backgroundColor = colorResource(id = R.color.colorGreen),
- modifier = Modifier
- .align(Alignment.End)
- .padding(16.dp, 16.dp, 16.dp, 16.dp),
- onClick = {
- openSheet(BottomSheetScreen.ScreenAddNewAccount)
- // navController.navigate(
- // R.id.action_navigation_profile_to_fragmentAdd,
- // )
- }
- )
- }
- }
- }
- }
|