Initial commit
This commit is contained in:
commit
cda6eb7ce0
680 changed files with 75081 additions and 0 deletions
272
app/src/main/java/com/mistral/chat/ui/MainActivity.kt
Normal file
272
app/src/main/java/com/mistral/chat/ui/MainActivity.kt
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
package com.mistral.chat.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.AutoCompleteTextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.mistral.chat.R
|
||||
import com.mistral.chat.api.MistralClient
|
||||
import com.mistral.chat.data.Message
|
||||
import com.mistral.chat.data.UserProfile
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var adapter: MessageAdapter
|
||||
private lateinit var inputField: TextInputEditText
|
||||
private lateinit var sendButton: MaterialButton
|
||||
private lateinit var modelSelector: AutoCompleteTextView
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private lateinit var progressIndicator: LinearProgressIndicator
|
||||
|
||||
private var client: MistralClient? = null
|
||||
private val messages = mutableListOf<Message>()
|
||||
private var availableModels: List<Pair<String, String>> = emptyList()
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
companion object {
|
||||
private const val API_KEY = "YW0IjDBRLuyEBcgNjVeVUFlMI6fcZYLA"
|
||||
private const val PREFS_NAME = "mistral_chat_prefs"
|
||||
private const val KEY_USER_NAME = "user_name"
|
||||
private const val KEY_USER_BIO = "user_bio"
|
||||
private const val KEY_USER_PREFS = "user_preferences"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
client = MistralClient(API_KEY)
|
||||
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
recyclerView = findViewById(R.id.recyclerView)
|
||||
inputField = findViewById(R.id.inputField)
|
||||
sendButton = findViewById(R.id.sendButton)
|
||||
modelSelector = findViewById(R.id.modelSelector)
|
||||
progressIndicator = findViewById(R.id.progressIndicator)
|
||||
|
||||
setupToolbar()
|
||||
setupRecyclerView()
|
||||
loadModels()
|
||||
setupInput()
|
||||
|
||||
inputField.requestFocus()
|
||||
inputField.postDelayed({
|
||||
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(inputField, InputMethodManager.SHOW_IMPLICIT)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_profile -> {
|
||||
showProfileDialog()
|
||||
true
|
||||
}
|
||||
R.id.action_clear -> {
|
||||
showClearChatDialog()
|
||||
true
|
||||
}
|
||||
R.id.action_about -> {
|
||||
showAboutDialog()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
adapter = MessageAdapter(messages)
|
||||
recyclerView.layoutManager = LinearLayoutManager(this).apply {
|
||||
stackFromEnd = true
|
||||
}
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun setupInput() {
|
||||
sendButton.setOnClickListener {
|
||||
val userInput = inputField.text?.toString()?.trim()
|
||||
if (userInput.isNullOrEmpty()) return@setOnClickListener
|
||||
|
||||
addMessage(Message(content = userInput, isUser = true))
|
||||
inputField.text?.clear()
|
||||
|
||||
sendMessage(userInput)
|
||||
}
|
||||
|
||||
inputField.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
toolbar.title = "Mistral Chat"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadModels() {
|
||||
lifecycleScope.launch {
|
||||
val result = client?.getModels()
|
||||
result?.onSuccess { models ->
|
||||
availableModels = models
|
||||
runOnUiThread {
|
||||
val modelNames = models.map { it.second }
|
||||
val adapter = ArrayAdapter(this@MainActivity, android.R.layout.simple_dropdown_item_1line, modelNames)
|
||||
modelSelector.setAdapter(adapter)
|
||||
|
||||
val codestralIndex = models.indexOfFirst { it.first.contains("codestral", ignoreCase = true) }
|
||||
if (codestralIndex >= 0) {
|
||||
modelSelector.setText(modelNames[codestralIndex], false)
|
||||
} else if (modelNames.isNotEmpty()) {
|
||||
modelSelector.setText(modelNames[0], false)
|
||||
}
|
||||
}
|
||||
}?.onFailure {
|
||||
availableModels = MistralClient.AVAILABLE_MODELS
|
||||
runOnUiThread {
|
||||
val modelNames = availableModels.map { it.second }
|
||||
val adapter = ArrayAdapter(this@MainActivity, android.R.layout.simple_dropdown_item_1line, modelNames)
|
||||
modelSelector.setAdapter(adapter)
|
||||
|
||||
val codestralIndex = availableModels.indexOfFirst { it.first.contains("codestral", ignoreCase = true) }
|
||||
if (codestralIndex >= 0) {
|
||||
modelSelector.setText(modelNames[codestralIndex], false)
|
||||
} else {
|
||||
modelSelector.setText(modelNames[0], false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMessage(message: Message) {
|
||||
messages.add(message)
|
||||
adapter.notifyItemInserted(messages.size - 1)
|
||||
recyclerView.scrollToPosition(messages.size - 1)
|
||||
}
|
||||
|
||||
private fun sendMessage(userInput: String) {
|
||||
val selectedModelName = modelSelector.text.toString()
|
||||
|
||||
val matchedModel = availableModels.find { it.second == selectedModelName }
|
||||
val selectedModel = matchedModel?.first ?: "mistral-small-latest"
|
||||
|
||||
val userProfile = loadUserProfile()
|
||||
val profileContext = if (!userProfile.isEmpty()) {
|
||||
"\n[User Profile]\n${userProfile.toContextString()}\n"
|
||||
} else ""
|
||||
|
||||
val apiMessages = messages.map { msg ->
|
||||
Message(
|
||||
content = msg.content,
|
||||
isUser = msg.isUser
|
||||
)
|
||||
}.toMutableList()
|
||||
|
||||
if (profileContext.isNotEmpty() && apiMessages.isEmpty()) {
|
||||
apiMessages.add(0, Message(content = profileContext, isUser = true))
|
||||
}
|
||||
|
||||
sendButton.isEnabled = false
|
||||
progressIndicator.isVisible = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
val result = client?.chat(selectedModel, apiMessages)
|
||||
|
||||
result?.onSuccess { (response, usedModel) ->
|
||||
addMessage(Message(content = response, isUser = false, senderName = usedModel))
|
||||
}?.onFailure { error ->
|
||||
addMessage(Message(content = "Error: ${error.message}", isUser = false, senderName = "Error"))
|
||||
}
|
||||
|
||||
sendButton.isEnabled = true
|
||||
progressIndicator.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadUserProfile(): UserProfile {
|
||||
return UserProfile(
|
||||
name = prefs.getString(KEY_USER_NAME, "") ?: "",
|
||||
bio = prefs.getString(KEY_USER_BIO, "") ?: "",
|
||||
preferences = prefs.getString(KEY_USER_PREFS, "") ?: ""
|
||||
)
|
||||
}
|
||||
|
||||
private fun saveUserProfile(profile: UserProfile) {
|
||||
prefs.edit()
|
||||
.putString(KEY_USER_NAME, profile.name)
|
||||
.putString(KEY_USER_BIO, profile.bio)
|
||||
.putString(KEY_USER_PREFS, profile.preferences)
|
||||
.apply()
|
||||
}
|
||||
|
||||
private fun deleteUserProfile() {
|
||||
prefs.edit()
|
||||
.remove(KEY_USER_NAME)
|
||||
.remove(KEY_USER_BIO)
|
||||
.remove(KEY_USER_PREFS)
|
||||
.apply()
|
||||
}
|
||||
|
||||
private fun showProfileDialog() {
|
||||
val profile = loadUserProfile()
|
||||
val dialogView = layoutInflater.inflate(R.layout.dialog_profile, null)
|
||||
|
||||
val nameInput = dialogView.findViewById<TextInputEditText>(R.id.nameInput)
|
||||
val bioInput = dialogView.findViewById<TextInputEditText>(R.id.bioInput)
|
||||
val preferencesInput = dialogView.findViewById<TextInputEditText>(R.id.preferencesInput)
|
||||
|
||||
nameInput.setText(profile.name)
|
||||
bioInput.setText(profile.bio)
|
||||
preferencesInput.setText(profile.preferences)
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(R.string.save) { _, _ ->
|
||||
val newProfile = UserProfile(
|
||||
name = nameInput.text?.toString() ?: "",
|
||||
bio = bioInput.text?.toString() ?: "",
|
||||
preferences = preferencesInput.text?.toString() ?: ""
|
||||
)
|
||||
saveUserProfile(newProfile)
|
||||
toolbar.subtitle = if (newProfile.name.isNotBlank()) newProfile.name else null
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setNeutralButton(R.string.delete) { _, _ ->
|
||||
deleteUserProfile()
|
||||
toolbar.subtitle = null
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showClearChatDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(R.string.clear_chat_confirm)
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
messages.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showAboutDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(R.string.about_text)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue