354 lines
15 KiB
Kotlin
354 lines
15 KiB
Kotlin
package de.twomartens.troubleshooting
|
|
|
|
import android.os.Bundle
|
|
import android.view.View
|
|
import android.widget.ArrayAdapter
|
|
import android.widget.Spinner
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
import androidx.lifecycle.Observer
|
|
import androidx.lifecycle.ViewModelProvider
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import com.udevel.widgetlab.TypingIndicatorView
|
|
import de.twomartens.troubleshooting.databinding.ActivityMessageListBinding
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.launch
|
|
|
|
class MessageListActivity : AppCompatActivity() {
|
|
private lateinit var recyclerView: RecyclerView
|
|
private lateinit var binding: ActivityMessageListBinding
|
|
private val viewAdapter: MessageListAdapter = MessageListAdapter()
|
|
private lateinit var spinner: Spinner
|
|
private lateinit var horizontalLayout: ConstraintLayout
|
|
private lateinit var typingIndicator: TypingIndicatorView
|
|
|
|
private lateinit var model: MessageListViewModel
|
|
|
|
private lateinit var adapter: ArrayAdapter<SpinnerOption>
|
|
|
|
private var showSpinner = true
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
binding = ActivityMessageListBinding.inflate(layoutInflater)
|
|
setContentView(binding.root)
|
|
|
|
initVariables()
|
|
adapter = initSpinner(model, spinner)
|
|
bindMessageObserver(viewAdapter, model)
|
|
initButton(::step, spinner)
|
|
val messages = initFirstChoice(adapter, model, horizontalLayout)
|
|
receiveMessages(messages, model, typingIndicator, recyclerView, horizontalLayout)
|
|
}
|
|
|
|
private fun initVariables() {
|
|
val viewManager = LinearLayoutManager(this)
|
|
viewManager.stackFromEnd = true
|
|
viewManager.reverseLayout = false
|
|
|
|
model = ViewModelProvider(this)[MessageListViewModel::class.java]
|
|
|
|
horizontalLayout = binding.layoutChatbox
|
|
typingIndicator = binding.indicator
|
|
typingIndicator.visibility = View.GONE
|
|
horizontalLayout.visibility = View.GONE
|
|
spinner = binding.spinner
|
|
|
|
recyclerView = binding.messageList.apply {
|
|
// 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
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
private fun initButton(step: (SpinnerOption) -> Unit, spinner: Spinner) {
|
|
binding.buttonSend.setOnClickListener {
|
|
val option = spinner.selectedItem
|
|
if (option is SpinnerOption) {
|
|
step(option)
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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)
|
|
}
|
|
OptionType.PROBLEM -> {
|
|
model.problem.value = Problem.valueOf(selectedOption.id)
|
|
problemSelected(model, adapter)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
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()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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())
|
|
)
|
|
}
|
|
|
|
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))
|
|
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())
|
|
)
|
|
}
|
|
|
|
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())
|
|
)
|
|
}
|
|
|
|
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))
|
|
adapter.notifyDataSetChanged()
|
|
return listOf(Message(getString(R.string.okay), false, System.currentTimeMillis()))
|
|
}
|
|
|
|
private fun nextSolutionQuestion(model: MessageListViewModel, adapter: ArrayAdapter<SpinnerOption>): List<Message> {
|
|
if (model.nextSolutionIndex.value == model.solutions.value?.size) {
|
|
// we exhausted all options
|
|
var summary = """
|
|
Component: ${model.component.value.toString()}
|
|
Problem: ${model.problem.value.toString()}
|
|
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)
|
|
}
|
|
}
|
|
|
|
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())
|
|
)
|
|
}
|
|
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))
|
|
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())
|
|
)
|
|
}
|
|
}
|
|
}
|