Skip to content

Commit 946d3a2

Browse files
authored
Merge pull request #6 from yml-org/jira/CM-1120/android-ui-fixes
[Android] CM-1120 - Android UI fixes
2 parents e7009ee + 5a12474 commit 946d3a2

18 files changed

+386
-194
lines changed

android-sample/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ dependencies {
5252
implementation(Dependencies.UI.COMPOSE_MATERIAL)
5353
implementation(Dependencies.UI.COMPOSE_ACTIVITY)
5454
implementation(Dependencies.UI.COMPOSE_NAVIGATION)
55+
implementation(Dependencies.UI.COMPOSE_LIVEDATA)
56+
implementation(Dependencies.DI.KOIN_CORE)
57+
implementation(Dependencies.DI.KOIN_ANDROID)
58+
implementation(Dependencies.DI.KOIN_COMPOSE)
5559
}

android-sample/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<uses-permission android:name="android.permission.INTERNET" />
55

66
<application
7+
android:name=".AppApplication"
78
android:allowBackup="false"
89
android:supportsRtl="true"
910
android:theme="@style/AppTheme">
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package co.yml.ychatgpt.android
2+
3+
import android.app.Application
4+
import co.yml.ychatgpt.android.di.appModule
5+
import org.koin.android.ext.koin.androidContext
6+
import org.koin.android.ext.koin.androidLogger
7+
import org.koin.core.context.startKoin
8+
9+
class AppApplication : Application() {
10+
11+
override fun onCreate() {
12+
super.onCreate()
13+
startKoin {
14+
androidLogger()
15+
androidContext(this@AppApplication)
16+
modules(appModule)
17+
}
18+
}
19+
}

android-sample/src/main/java/co/yml/ychatgpt/android/MainActivity.kt

Lines changed: 2 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -3,189 +3,17 @@ package co.yml.ychatgpt.android
33
import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
6-
import androidx.compose.foundation.Image
7-
import androidx.compose.foundation.layout.Arrangement
8-
import androidx.compose.foundation.layout.Column
9-
import androidx.compose.foundation.layout.Row
10-
import androidx.compose.foundation.layout.Spacer
11-
import androidx.compose.foundation.layout.fillMaxSize
12-
import androidx.compose.foundation.layout.fillMaxWidth
13-
import androidx.compose.foundation.layout.height
14-
import androidx.compose.foundation.layout.padding
15-
import androidx.compose.foundation.layout.width
16-
import androidx.compose.material.MaterialTheme
17-
import androidx.compose.material.Scaffold
18-
import androidx.compose.material.Surface
19-
import androidx.compose.material.Text
20-
import androidx.compose.material.icons.Icons
21-
import androidx.compose.material.icons.filled.Edit
22-
import androidx.compose.material.rememberScaffoldState
23-
import androidx.compose.runtime.Composable
24-
import androidx.compose.runtime.LaunchedEffect
25-
import androidx.compose.runtime.getValue
26-
import androidx.compose.runtime.mutableStateListOf
27-
import androidx.compose.runtime.mutableStateOf
28-
import androidx.compose.runtime.remember
29-
import androidx.compose.runtime.rememberCoroutineScope
30-
import androidx.compose.runtime.setValue
31-
import androidx.compose.ui.Alignment
32-
import androidx.compose.ui.Modifier
33-
import androidx.compose.ui.res.painterResource
34-
import androidx.compose.ui.res.stringResource
35-
import androidx.compose.ui.text.font.FontWeight
36-
import androidx.compose.ui.unit.sp
376
import androidx.lifecycle.lifecycleScope
38-
import androidx.navigation.NavHostController
39-
import androidx.navigation.compose.NavHost
40-
import androidx.navigation.compose.composable
41-
import androidx.navigation.compose.rememberNavController
427
import co.yml.ychatgpt.ChatGpt
43-
import co.yml.ychatgpt.android.ui.AppBar
44-
import co.yml.ychatgpt.android.ui.ChatLayout
45-
import co.yml.ychatgpt.android.ui.Dimensions.spaceMedium
46-
import co.yml.ychatgpt.android.ui.Dimensions.splashIconSize
47-
import co.yml.ychatgpt.android.ui.DrawerBody
48-
import co.yml.ychatgpt.android.ui.DrawerHeader
49-
import co.yml.ychatgpt.android.ui.SendMessageLayout
50-
import kotlinx.coroutines.delay
51-
import kotlinx.coroutines.launch
52-
import kotlin.coroutines.CoroutineContext
8+
import org.koin.androidx.viewmodel.ext.android.viewModel
539

5410
class MainActivity : ComponentActivity() {
5511

56-
private val chatGpt by lazy { ChatGpt.create(BuildConfig.API_KEY) }
57-
58-
private val myCoroutineContext by lazy { lifecycleScope.coroutineContext }
59-
6012
override fun onCreate(savedInstanceState: Bundle?) {
6113
super.onCreate(savedInstanceState)
6214
setContent {
6315
MyApplicationTheme() {
64-
Navigation(chatGpt, myCoroutineContext)
65-
}
66-
}
67-
}
68-
}
69-
70-
@Composable
71-
fun Navigation(chatGpt: ChatGpt, myCoroutineContext: CoroutineContext) {
72-
val navController = rememberNavController()
73-
74-
NavHost(
75-
navController = navController,
76-
startDestination = "splash_screen"
77-
) {
78-
79-
composable("splash_screen") {
80-
SplashScreen(navController)
81-
}
82-
83-
composable("main_screen") {
84-
MainScreen(chatGpt, myCoroutineContext)
85-
}
86-
}
87-
88-
}
89-
90-
@Composable
91-
fun MainScreen(chatGpt: ChatGpt, myCoroutineContext: CoroutineContext) {
92-
val scaffoldState = rememberScaffoldState()
93-
val scope = rememberCoroutineScope()
94-
var chatGptAnswer by remember {
95-
mutableStateOf("")
96-
}
97-
val items = remember {
98-
mutableStateListOf<MessageItem>()
99-
}
100-
Scaffold(
101-
scaffoldState = scaffoldState,
102-
topBar = {
103-
AppBar(
104-
onNavigationItemClick = {
105-
scope.launch {
106-
scaffoldState.drawerState.open()
107-
}
108-
}
109-
)
110-
},
111-
drawerContent = {
112-
DrawerHeader()
113-
DrawerBody(items = listOf(
114-
MenuItem(
115-
id = "completion",
116-
title = stringResource(R.string.completion),
117-
contentDescription = "completion",
118-
icon = Icons.Default.Edit
119-
),
120-
MenuItem(
121-
id = "edits",
122-
title = stringResource(R.string.edits),
123-
contentDescription = "edit",
124-
icon = Icons.Default.Edit
125-
)
126-
127-
), onItemClick = {
128-
// do nothing
129-
})
130-
},
131-
content = { padding ->
132-
Column(
133-
modifier = Modifier
134-
.fillMaxSize()
135-
.padding(padding),
136-
verticalArrangement = Arrangement.Center,
137-
horizontalAlignment = Alignment.CenterHorizontally
138-
) {
139-
ChatLayout(items)
140-
}
141-
},
142-
bottomBar = {
143-
SendMessageLayout(onSendMessage = {
144-
scope.launch {
145-
items.add(MessageItem(message = it, isOut = true))
146-
chatGptAnswer = chatGpt.completion(it)
147-
items.add(MessageItem(message = chatGptAnswer, isOut = false))
148-
}
149-
})
150-
},
151-
)
152-
}
153-
154-
@Composable
155-
fun SplashScreen(navController: NavHostController) {
156-
157-
LaunchedEffect(key1 = true) {
158-
delay(2000L)
159-
navController.navigate("main_screen")
160-
}
161-
162-
Surface(
163-
modifier = Modifier.fillMaxWidth(),
164-
color = MaterialTheme.colors.background,
165-
) {
166-
Column(
167-
modifier = Modifier.fillMaxSize(),
168-
verticalArrangement = Arrangement.Center,
169-
horizontalAlignment = Alignment.CenterHorizontally,
170-
) {
171-
Row(
172-
modifier = Modifier.fillMaxWidth(),
173-
horizontalArrangement = Arrangement.Center,
174-
verticalAlignment = Alignment.CenterVertically
175-
) {
176-
Image(
177-
painterResource(R.drawable.ic_chat),
178-
contentDescription = stringResource(R.string.logo),
179-
modifier = Modifier
180-
.width(splashIconSize)
181-
.height(splashIconSize)
182-
)
183-
Spacer(modifier = Modifier.width(spaceMedium))
184-
Text(
185-
text = stringResource(R.string.ychat_gpt),
186-
fontSize = 26.sp,
187-
fontWeight = FontWeight.Bold
188-
)
16+
Navigation()
18917
}
19018
}
19119
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package co.yml.ychatgpt.android
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.navigation.compose.NavHost
5+
import androidx.navigation.compose.composable
6+
import androidx.navigation.compose.rememberNavController
7+
import co.yml.ychatgpt.ChatGpt
8+
import co.yml.ychatgpt.android.ui.MainScreen
9+
import co.yml.ychatgpt.android.ui.SplashScreen
10+
import kotlin.coroutines.CoroutineContext
11+
12+
@Composable
13+
fun Navigation() {
14+
val navController = rememberNavController()
15+
16+
NavHost(
17+
navController = navController,
18+
startDestination = "splash_screen"
19+
) {
20+
21+
composable("splash_screen") {
22+
SplashScreen(navController)
23+
}
24+
25+
composable("main_screen") {
26+
MainScreen()
27+
}
28+
}
29+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package co.yml.ychatgpt.android
2+
3+
import androidx.compose.runtime.mutableStateListOf
4+
import androidx.compose.runtime.mutableStateOf
5+
import androidx.lifecycle.MutableLiveData
6+
import androidx.lifecycle.ViewModel
7+
import androidx.lifecycle.viewModelScope
8+
import co.yml.ychatgpt.ChatGpt
9+
import kotlinx.coroutines.delay
10+
import kotlinx.coroutines.launch
11+
12+
class MainViewModel(private val chatGpt: ChatGpt) : ViewModel() {
13+
14+
private val _items = mutableStateListOf<MessageItem>()
15+
val items = _items
16+
17+
private val _isLoading = MutableLiveData(false)
18+
val isLoading get() = _isLoading
19+
20+
private var typingTxt = mutableStateOf("")
21+
private var typingItem = mutableStateOf(MessageItem(message = typingTxt.value, isOut = false))
22+
23+
private fun setLoading(isLoading: Boolean) {
24+
_isLoading.value = isLoading
25+
}
26+
27+
fun onSendMessage(message: String, typingStr: String) {
28+
updateTypingMessage(typingStr)
29+
viewModelScope.launch {
30+
showTypingAnimation(message)
31+
writeResponse(requestCompletion(message))
32+
}
33+
}
34+
35+
private suspend fun showTypingAnimation(message: String) {
36+
items.add(MessageItem(message = message, isOut = true))
37+
delay((1000..2000).random().toLong())
38+
setLoading(true)
39+
items.add(typingItem.value)
40+
}
41+
42+
private fun writeResponse(chatGptAnswer: String) {
43+
items.remove(items[items.lastIndex])
44+
items.add(MessageItem(message = chatGptAnswer, isOut = false))
45+
setLoading(false)
46+
}
47+
48+
private suspend fun requestCompletion(message: String): String {
49+
return try {
50+
chatGpt.completion(message)
51+
} catch (e: Exception) {
52+
e.message ?: ERROR
53+
}
54+
}
55+
56+
fun updateTyping(string: String) {
57+
typingTxt.value = if (typingTxt.value.endsWith("...")) string else "${typingTxt.value}."
58+
items[items.lastIndex] = items.last().copy(message = typingTxt.value)
59+
}
60+
61+
private fun updateTypingMessage(typingStr: String) {
62+
typingTxt.value = typingStr
63+
typingItem.value = MessageItem(message = typingTxt.value, isOut = false)
64+
}
65+
66+
companion object {
67+
private const val ERROR = "Error"
68+
}
69+
}

android-sample/src/main/java/co/yml/ychatgpt/android/MyApplicationTheme.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import androidx.compose.material.Typography
88
import androidx.compose.material.darkColors
99
import androidx.compose.material.lightColors
1010
import androidx.compose.runtime.Composable
11-
import androidx.compose.ui.graphics.Color
1211
import androidx.compose.ui.res.colorResource
12+
import androidx.compose.ui.res.dimensionResource
1313
import androidx.compose.ui.text.TextStyle
1414
import androidx.compose.ui.text.font.FontFamily
1515
import androidx.compose.ui.text.font.FontWeight
@@ -41,7 +41,7 @@ fun MyApplicationTheme(
4141
body1 = TextStyle(
4242
fontFamily = FontFamily.Default,
4343
fontWeight = FontWeight.Normal,
44-
fontSize = 16.sp
44+
fontSize = dimensionResource(id = R.dimen.font14).value.sp
4545
)
4646
)
4747
val shapes = Shapes(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package co.yml.ychatgpt.android.di
2+
3+
import co.yml.ychatgpt.ChatGpt
4+
import co.yml.ychatgpt.android.BuildConfig
5+
import co.yml.ychatgpt.android.MainViewModel
6+
import org.koin.androidx.viewmodel.dsl.viewModel
7+
import org.koin.androidx.viewmodel.dsl.viewModelOf
8+
import org.koin.dsl.module
9+
10+
val appModule = module {
11+
single { ChatGpt.create(BuildConfig.API_KEY) }
12+
viewModelOf(::MainViewModel)
13+
}

0 commit comments

Comments
 (0)