[여러가지 시도]/코틀린 결과물

[결과물_앱] 챗봇앱

시간 확보러 2022. 11. 12. 07:00
728x90

와.... 정말 쉽지 않았다.

그저 보면서 하는데도.. 왜 나는 에러가 생길까...

그래도 끝까지 해내고 나니까 뿌듯하다.

 

정말 에러가 생기면 그만두고 싶은 마음이 굴뚝같다.

하지만... 뭔가 시작했으니까 끝까지 하고 싶은 마음으로 하다보니

몇주가 걸렸다...

늦어지더라도 끝까지 하는게 중요하다!

 

 

package com.jeong.mychatbottutorial

import android.inputmethodservice.Keyboard
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.jeong.mychatbottutorial.MainActivity.Companion.TAG
import com.jeong.mychatbottutorial.data.model.Msg
import com.jeong.mychatbottutorial.data.model.MsgType
import com.jeong.mychatbottutorial.ui.theme.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

    companion object {
        const val TAG = "챗봇"
    }

    private val chatBotViewModel: ChatBotViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

        setContent {
            MyChatBotTutorialTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    ChatBotView(chatBotViewModel)
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ChatBotView(chatBotViewModel: ChatBotViewModel){

    val userInput : State<String> = chatBotViewModel.userInputFlow.collectAsState()

    val isSendBtnEnable :  State<Boolean> = chatBotViewModel.isSendBtnEnableFlow.collectAsState()

    val messages: State<List<Msg>> = chatBotViewModel.messagesFlow.collectAsState()

    val isLoading : State<Boolean> = chatBotViewModel.isLoading.collectAsState()

//    var userInput by remember {
//        mutableStateOf("사용자 입력")
//    }

//    val numersRange = (1..100)
//
//    val dummyMessages = numersRange.mapIndexed{ index, number ->
//
//        val type = if (index % 2 == 0) MsgType.BOT else MsgType.ME
//
//        Msg("$number 번째 입니다!", type)
//    }













    val coroutineScope = rememberCoroutineScope()
    val scrollState = rememberLazyListState()

    val focusManager = LocalFocusManager.current

    LaunchedEffect(key1 = Unit){
        chatBotViewModel.msgReceivedEvent.collectLatest {
            scrollState.animateScrollToItem(messages.value.size)
        }
    }


    Column(
        modifier = Modifier.fillMaxSize()
    ){

        LazyColumn (
            state = scrollState,
            modifier = Modifier.weight(1f),
            verticalArrangement = Arrangement.spacedBy(8.dp,
                alignment = Alignment.Top),
            contentPadding = PaddingValues(10.dp)
                ){
            items(messages.value){ MsgRowItem(it) }
        }


        Divider (
            startIndent = 0.dp,
            thickness = 1.dp,
            color = Color.LightGray

        )


        Row(
            modifier = Modifier
                .fillMaxWidth()
                .height(IntrinsicSize.Max)
                .background(Color.White),
            verticalAlignment = Alignment.Top

        ){
            BasicTextField(
                modifier = Modifier
                    .background(Color.White)
                    .weight(1f)
                    .padding(12.dp)
                ,
                value =userInput.value,
                onValueChange ={ justTypeUserInput ->
                chatBotViewModel.userInputFlow.value = justTypeUserInput
            } )
            Card(
                backgroundColor = if (isSendBtnEnable.value) SendBtBGColor else Color.LightGray,
                onClick = {
                    Log.d(TAG, "전송하기 버튼 클릭!")
                    if (!isSendBtnEnable.value){ return@Card }
                    chatBotViewModel.sendMessage()

//                    if (userInput.value.isEmpty()){ return@Card
//
//                    }
//                    messages.value += Msg(userInput, MsgType.ME)
//                    userInput =""
                    focusManager.clearFocus()
                        coroutineScope.launch {
                    scrollState.animateScrollToItem(messages.value.size)
                }
        }


            ){

                if (isLoading.value){
                    CircularProgressIndicator(
                        color = BotBubbleColor,
                        modifier = Modifier
                            .padding(5.dp)
                            .scale(0.6f)
                            .requiredHeight(IntrinsicSize.Max)
                    )
                } else {
                    Image(
                        painter = painterResource(id = R.drawable.ic_send_message),
                        contentDescription = "보내기버튼",
                        modifier = Modifier
                            .padding(10.dp)
                            .width(30.dp)
                    )
                }


            }
        }
    }
}

@Composable
fun MsgRowItem(data: Msg){

    val itemArrangement : (MsgType) -> Arrangement.Horizontal = {
        if (data.type == MsgType.ME) Arrangement.End else Arrangement.Start
    }

    Row(
        modifier = Modifier.fillMaxSize(),
        horizontalArrangement = itemArrangement(data.type)
    ){
        when (data.type) {
            MsgType.ME -> MeMsgRowItem(message = data.content)
            MsgType.BOT -> BotMsgRowItem(message = data.content)
        }
    }
}

@Composable
fun MeMsgRowItem(message: String){

    Row(
        modifier = Modifier
            .height(IntrinsicSize.Max)
            .fillMaxWidth(0.7f)
            .padding(end = 0.dp)
        ,
        verticalAlignment = Alignment.Bottom,
        horizontalArrangement = Arrangement.End
    ){
        Box(
            modifier = Modifier
                .background(
                    color = MeButtleColor,
                    shape = RoundedCornerShape(12.dp, 12.dp, 0.dp, 12.dp)
                )
                .width(IntrinsicSize.Max)
        ){
            Text(text = message, modifier = Modifier.padding(16.dp))
        }

        Box(
            modifier = Modifier
                .background(
                    color = MeButtleColor,
                    shape = TriangleShapeForRight(30)
                )
                .width(30.dp)
                .fillMaxHeight()
        ){}
    }



}

@Composable
fun BotMsgRowItem(message: String){
    Row(
        modifier = Modifier
            .height(IntrinsicSize.Max)
            .fillMaxWidth(0.7f)
        ,
        verticalAlignment = Alignment.Bottom,
        horizontalArrangement = Arrangement.Start
    ){

        Image(
            painter = painterResource(id = R.drawable.robot),
            contentDescription = "bot",
            modifier = Modifier
                .size(60.dp)
                .clip(CircleShape)
                .border(BorderStroke(2.dp, Color.LightGray), shape = CircleShape)
        )


        Spacer(modifier = Modifier.width(8.dp))


        Box(
            modifier = Modifier
                .background(
                    color = BotBubbleColor,
                    shape = TriangleShapeForLeft(30)
                )
                .width(10.dp)
                .fillMaxHeight()
        ){}


        Box(
            modifier = Modifier
                .background(
                    color = BotBubbleColor,
                    shape = RoundedCornerShape(12.dp, 12.dp, 12.dp, 0.dp)
                )
                .width(IntrinsicSize.Max)
        ){
            Text(
                text = message,
                modifier = Modifier.padding(16.dp),
                color = Color.White
                )
        }


    }
}


@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyChatBotTutorialTheme {
        Greeting("Android")
    }
}
728x90

'[여러가지 시도] > 코틀린 결과물' 카테고리의 다른 글

[결과물_앱] 계산기  (0) 2022.10.31
[결과물_앱] 타이머  (0) 2022.10.29
[결과물_앱] 로또번호 생성기  (0) 2022.10.28