troubleshooting-agent/app/src/main/java/de/twomartens/troubleshooting/MessageListActivity.kt

354 lines
15 KiB
Kotlin
Raw Normal View History

package de.twomartens.troubleshooting
2019-07-14 21:46:36 +02:00
import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import android.widget.Spinner
2022-11-27 20:28:39 +01:00
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Observer
2022-11-27 20:28:39 +01:00
import androidx.lifecycle.ViewModelProvider
2019-07-14 21:46:36 +02:00
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
2019-07-16 18:06:25 +02:00
import com.udevel.widgetlab.TypingIndicatorView
2022-11-27 20:28:39 +01:00
import de.twomartens.troubleshooting.databinding.ActivityMessageListBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
2019-07-14 21:46:36 +02:00
class MessageListActivity : AppCompatActivity() {
2019-07-14 21:46:36 +02:00
private lateinit var recyclerView: RecyclerView
2022-11-27 20:28:39 +01:00
private lateinit var binding: ActivityMessageListBinding
private val viewAdapter: MessageListAdapter = MessageListAdapter()
2019-07-14 21:46:36 +02:00
private lateinit var spinner: Spinner
private lateinit var horizontalLayout: ConstraintLayout
2019-07-16 18:06:25 +02:00
private lateinit var typingIndicator: TypingIndicatorView
private lateinit var model: MessageListViewModel
private lateinit var adapter: ArrayAdapter<SpinnerOption>
2019-07-14 21:46:36 +02:00
private var showSpinner = true
2019-07-14 21:46:36 +02:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
2022-11-27 20:28:39 +01:00
binding = ActivityMessageListBinding.inflate(layoutInflater)
setContentView(binding.root)
2019-07-14 21:46:36 +02:00
initVariables()
adapter = initSpinner(model, spinner)
bindMessageObserver(viewAdapter, model)
2022-11-27 20:28:39 +01:00
initButton(::step, spinner)
val messages = initFirstChoice(adapter, model, horizontalLayout)
receiveMessages(messages, model, typingIndicator, recyclerView, horizontalLayout)
}
private fun initVariables() {
2019-07-14 21:46:36 +02:00
val viewManager = LinearLayoutManager(this)
viewManager.stackFromEnd = true
viewManager.reverseLayout = false
2022-11-27 20:28:39 +01:00
model = ViewModelProvider(this)[MessageListViewModel::class.java]
2022-11-27 20:28:39 +01:00
horizontalLayout = binding.layoutChatbox
typingIndicator = binding.indicator
2019-07-16 18:06:25 +02:00
typingIndicator.visibility = View.GONE
horizontalLayout.visibility = View.GONE
2022-11-27 20:28:39 +01:00
spinner = binding.spinner
2019-07-14 21:46:36 +02:00
2022-11-27 20:28:39 +01:00
recyclerView = binding.messageList.apply {
2019-07-14 21:46:36 +02:00
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
setHasFixedSize(true)
// use a linear layout manager
layoutManager = viewManager
// specify an viewAdapter (see also next example)
adapter = viewAdapter
}
}
2019-07-14 21:46:36 +02:00
private fun initSpinner(model: MessageListViewModel,
spinner: Spinner): ArrayAdapter<SpinnerOption> {
val spinnerOptions: ArrayList<SpinnerOption>? = model.spinnerOptions.value
if (spinnerOptions != null) {
val arrayAdapter = ArrayAdapter(this, R.layout.spinner_item, spinnerOptions)
// Specify the layout to use when the list of choices appears
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
arrayAdapter.setNotifyOnChange(true)
// Apply the adapter to the spinner
spinner.adapter = arrayAdapter
return arrayAdapter
2019-07-14 21:46:36 +02:00
}
return ArrayAdapter(this, android.R.layout.simple_spinner_item, ArrayList<SpinnerOption>())
}
private fun bindMessageObserver(viewAdapter: MessageListAdapter, model: MessageListViewModel) {
val messageObserver = Observer<ArrayList<Message>> { messages ->
viewAdapter.replaceMessages(messages)
}
model.getMessages().observe(this, messageObserver)
}
2022-11-27 20:28:39 +01:00
private fun initButton(step: (SpinnerOption) -> Unit, spinner: Spinner) {
binding.buttonSend.setOnClickListener {
val option = spinner.selectedItem
if (option is SpinnerOption) {
step(option)
}
}
2019-07-14 21:46:36 +02:00
}
private fun initFirstChoice(adapter: ArrayAdapter<SpinnerOption>,
model: MessageListViewModel,
horizontalLayout: ConstraintLayout): List<Message> {
val isInitialized = model.initialized.value
return if (isInitialized != null && !isInitialized) {
model.spinnerOptions.value?.addAll(SpinnerOption.getOptions(
resources.getStringArray(R.array.components_values),
resources.getStringArray(R.array.components),
OptionType.COMPONENT))
adapter.notifyDataSetChanged()
model.initialized.value = true
listOf(Message(getString(R.string.greeting), false, System.currentTimeMillis()))
} else {
val spinnerVisible = model.spinnerVisible.value
if (spinnerVisible != null && spinnerVisible) horizontalLayout.visibility = View.VISIBLE
listOf()
}
}
private fun step(selectedOption: SpinnerOption) {
sendMessage(selectedOption, model, recyclerView, horizontalLayout)
val messages = branch(
selectedOption,
model, adapter,
::componentSelected,
::problemSelected,
::didItWorkQuestion,
::pleaseTry,
::problemSolved,
::nextSolutionQuestion,
::doNotShowSpinner)
receiveMessages(messages, model, typingIndicator, recyclerView, horizontalLayout, showSpinner)
}
private fun sendMessage(selectedOption: SpinnerOption, model: MessageListViewModel,
recyclerView: RecyclerView, horizontalLayout: ConstraintLayout) {
val message = Message(selectedOption.toString(), true, System.currentTimeMillis())
model.add(message)
recyclerView.scrollToPosition(model.size() - 1)
horizontalLayout.visibility = View.GONE
model.spinnerVisible.value = false
}
private fun receiveMessages(messages: List<Message>,
model: MessageListViewModel,
typingIndicator: View,
recyclerView: RecyclerView,
horizontalLayout: ConstraintLayout,
showSpinner: Boolean = true) {
model.uiScope.launch {
for (message in messages) {
typingIndicator.visibility = View.VISIBLE
delay(1500)
typingIndicator.visibility = View.GONE
model.add(message)
recyclerView.scrollToPosition(model.size() - 1)
}
if (messages.isNotEmpty() && showSpinner) {
horizontalLayout.visibility = View.VISIBLE
model.spinnerVisible.value = true
}
}
}
private fun doNotShowSpinner() {
showSpinner = false
2019-07-16 18:06:25 +02:00
}
private fun branch(selectedOption: SpinnerOption,
model: MessageListViewModel,
adapter: ArrayAdapter<SpinnerOption>,
componentSelected: (MessageListViewModel, ArrayAdapter<SpinnerOption>) -> List<Message>,
problemSelected: (MessageListViewModel, ArrayAdapter<SpinnerOption>) -> List<Message>,
didItWorkQuestion: (MessageListViewModel, ArrayAdapter<SpinnerOption>) -> List<Message>,
pleaseTry: () -> List<Message>,
problemSolved: (MessageListViewModel, ArrayAdapter<SpinnerOption>) -> List<Message>,
nextSolutionQuestion: (MessageListViewModel, ArrayAdapter<SpinnerOption>) -> List<Message>,
doNotShowSpinner: () -> Unit): List<Message> {
return when (selectedOption.type) {
OptionType.COMPONENT -> {
model.component.value = Component.valueOf(selectedOption.id)
componentSelected(model, adapter)
2019-07-15 20:57:46 +02:00
}
OptionType.PROBLEM -> {
model.problem.value = Problem.valueOf(selectedOption.id)
problemSelected(model, adapter)
2019-07-15 20:57:46 +02:00
}
OptionType.BINARY_HAVE_YOU_TRIED -> {
when (selectedOption.id) {
"YES" -> didItWorkQuestion(model, adapter)
else -> pleaseTry()
}
}
OptionType.BINARY_DID_IT_WORK -> {
when (selectedOption.id) {
"YES" -> problemSolved(model, adapter)
else -> nextSolutionQuestion(model, adapter)
}
2019-07-15 20:57:46 +02:00
}
OptionType.STORY -> {
doNotShowSpinner()
if (selectedOption.id == "WHO_ARE_YOU") {
listOf(
Message(
getString(R.string.gardner_second),
false,
System.currentTimeMillis()
)
)
}
else {
listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.im_gardner), false, System.currentTimeMillis()),
Message(getString(R.string.okay_saves_time), false, System.currentTimeMillis()),
Message(getString(R.string.its_funny), false, System.currentTimeMillis()))
}
2019-07-15 20:57:46 +02:00
}
}
}
private fun componentSelected(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.addAll(SpinnerOption.getOptions(
resources.getStringArray(resources.getIdentifier(
model.component.value?.name.plus("_problems_values"),
"array",
packageName)),
resources.getStringArray(resources.getIdentifier(
model.component.value?.name.plus("_problems"),
"array",
packageName)),
OptionType.PROBLEM))
adapter.notifyDataSetChanged()
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.select_problem), false, System.currentTimeMillis())
)
2019-07-14 21:46:36 +02:00
}
private fun problemSelected(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
model.solutions.value?.addAll(resources.getStringArray(resources.getIdentifier(
model.component.value?.name.plus("_").plus(model.problem.value?.name),
"array",
packageName
)))
val firstSolutionQuestion = getString(R.string.have_you_tried, model.solutions.value?. let { it[0] })
model.nextSolutionIndex.value = (model.nextSolutionIndex.value ?: 0) + 1
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.addAll(SpinnerOption.getOptions(
resources.getStringArray(R.array.binary_values),
resources.getStringArray(R.array.binary),
OptionType.BINARY_HAVE_YOU_TRIED))
2019-07-15 20:57:46 +02:00
adapter.notifyDataSetChanged()
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(firstSolutionQuestion, false, System.currentTimeMillis())
)
}
private fun didItWorkQuestion(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
model.currentSolutionState.value = State.DID_IT_WORK
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.addAll(SpinnerOption.getOptions(
resources.getStringArray(R.array.binary_values),
resources.getStringArray(R.array.binary),
OptionType.BINARY_DID_IT_WORK))
adapter.notifyDataSetChanged()
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.did_it_work), false, System.currentTimeMillis())
)
2019-07-15 20:57:46 +02:00
}
private fun pleaseTry(): List<Message> {
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.please_try), false, System.currentTimeMillis()),
Message(getString(R.string.i_wait), false, System.currentTimeMillis()),
Message(getString(R.string.have_you_tried, model.solutions.value?.let { it[(model.nextSolutionIndex.value ?: 1) - 1]}), false, System.currentTimeMillis())
)
2019-07-15 20:57:46 +02:00
}
private fun problemSolved(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.add(SpinnerOption(
"SAY_SOMETHING_DIFFERENT",
getString(R.string.can_you_say_sth_different),
OptionType.STORY))
2019-07-15 20:57:46 +02:00
adapter.notifyDataSetChanged()
return listOf(Message(getString(R.string.okay), false, System.currentTimeMillis()))
2019-07-15 20:57:46 +02:00
}
2019-07-14 21:46:36 +02:00
private fun nextSolutionQuestion(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
if (model.nextSolutionIndex.value == model.solutions.value?.size) {
2019-07-15 20:57:46 +02:00
// we exhausted all options
var summary = """
Component: ${model.component.value.toString()}
Problem: ${model.problem.value.toString()}
2019-07-15 20:57:46 +02:00
Tried Solutions:
""".trimIndent()
val solutions: ArrayList<CharSequence>? = model.solutions.value
if (solutions != null) {
for (solution in solutions) {
val item = "\n - $solution"
summary = summary.plus(item)
}
2019-07-14 21:46:36 +02:00
}
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.add(SpinnerOption(
"WHO_ARE_YOU",
getString(R.string.who_are_you),
OptionType.STORY))
adapter.notifyDataSetChanged()
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.sorry), false, System.currentTimeMillis()),
Message(getString(R.string.cant_help), false, System.currentTimeMillis()),
Message(getString(R.string.should_contact_support), false, System.currentTimeMillis()),
Message(getString(R.string.okay_wait), false, System.currentTimeMillis()),
Message(getString(R.string.tell_support), false, System.currentTimeMillis()),
Message(summary, false, System.currentTimeMillis())
)
2019-07-15 20:57:46 +02:00
}
else {
val solutionQuestion = getString(R.string.have_you_tried, model.solutions.value?.let {it[model.nextSolutionIndex.value ?: 0]})
model.nextSolutionIndex.value = (model.nextSolutionIndex.value ?: 0) + 1
model.currentSolutionState.value = State.HAVE_YOU_TRIED
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.addAll(SpinnerOption.getOptions(
resources.getStringArray(R.array.binary_values),
resources.getStringArray(R.array.binary),
OptionType.BINARY_HAVE_YOU_TRIED))
2019-07-15 20:57:46 +02:00
adapter.notifyDataSetChanged()
return listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.sorry), false, System.currentTimeMillis()),
Message(solutionQuestion, false, System.currentTimeMillis())
)
2019-07-15 20:57:46 +02:00
}
}
2019-07-14 21:46:36 +02:00
}