Finished app state persistence and improved user interaction

This commit is contained in:
Jim Martens 2019-07-16 21:27:59 +02:00
parent 1786c5c12a
commit 44cf06c9a6
2 changed files with 114 additions and 73 deletions

View File

@ -24,13 +24,7 @@ class MessageListActivity : AppCompatActivity() {
private lateinit var model: MessageListViewModel
private val spinnerOptions: ArrayList<CharSequence> = ArrayList()
private val solutions: ArrayList<CharSequence> = ArrayList()
private var nextSolutionIndex: Int = 0
private var currentSolutionState: State = State.HAVE_YOU_TRIED
private lateinit var adapter: ArrayAdapter<CharSequence>
private lateinit var component: Component
private lateinit var problem: Problem
override fun onCreate(savedInstanceState: Bundle?) {
@ -43,11 +37,11 @@ class MessageListActivity : AppCompatActivity() {
model = ViewModelProviders.of(this).get(MessageListViewModel::class.java)
initFirstChoice()
horizontalLayout = findViewById(R.id.layout_chatbox)
typingIndicator = findViewById(R.id.indicator)
typingIndicator.visibility = View.GONE
horizontalLayout.visibility = View.GONE
spinner = findViewById(R.id.spinner)
recyclerView = findViewById<RecyclerView>(R.id.messageList).apply {
// use this setting to improve performance if you know that changes
@ -59,14 +53,25 @@ class MessageListActivity : AppCompatActivity() {
// specify an viewAdapter (see also next example)
adapter = viewAdapter
}
val spinnerOptions: ArrayList<CharSequence>? = model.spinnerOptions.value
if (spinnerOptions != null) {
adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, spinnerOptions)
}
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
adapter.setNotifyOnChange(true)
// Apply the adapter to the spinner
spinner.adapter = adapter
val messageObserver = Observer<ArrayList<Message>> { messages ->
viewAdapter.replaceMessages(messages)
}
model.getMessages().observe(this, messageObserver)
initFirstChoice()
}
fun sendMessage(view: View) {
@ -74,16 +79,17 @@ class MessageListActivity : AppCompatActivity() {
val message = Message(text, true, System.currentTimeMillis())
model.add(message)
recyclerView.scrollToPosition(model.size() - 1)
viewAdapter.notifyItemInserted(model.size() - 1)
horizontalLayout.visibility = View.GONE
model.spinnerVisible.value = false
nextStage(text)
}
private fun receiveMessage(message: Message) {
receiveMessages(listOf(message))
private fun receiveMessage(message: Message, showSpinner: Boolean = true) {
receiveMessages(listOf(message), showSpinner)
}
private fun receiveMessages(messages: List<Message>) {
private fun receiveMessages(messages: List<Message>, showSpinner: Boolean = true) {
model.uiScope.launch {
for (message in messages) {
typingIndicator.visibility = View.VISIBLE
@ -92,6 +98,10 @@ class MessageListActivity : AppCompatActivity() {
model.add(message)
recyclerView.scrollToPosition(model.size() - 1)
}
if (showSpinner) {
horizontalLayout.visibility = View.VISIBLE
model.spinnerVisible.value = true
}
}
}
@ -100,7 +110,7 @@ class MessageListActivity : AppCompatActivity() {
when (selection) {
in resources.getStringArray(R.array.components) -> {
// first selection
component = Component.valueOf(selection.toUpperCase())
model.component.value = Component.valueOf(selection.toUpperCase())
secondChoice()
}
in resources.getStringArray(R.array.monitor_problems),
@ -109,12 +119,11 @@ class MessageListActivity : AppCompatActivity() {
in resources.getStringArray(R.array.tv_problems),
in resources.getStringArray(R.array.router_problems) -> {
// second selection
problem = Problem.valueOf(selection.toUpperCase().replace(' ', '_'))
receiveMessage(Message(getString(R.string.okay), false, System.currentTimeMillis()))
model.problem.value = Problem.valueOf(selection.toUpperCase().replace(' ', '_'))
solutionQuestion()
}
in resources.getStringArray(R.array.binary) -> {
when (currentSolutionState) {
when (model.currentSolutionState.value) {
State.HAVE_YOU_TRIED -> triedAnswer(selection)
State.DID_IT_WORK -> doesItWorkAnswer(selection)
}
@ -124,61 +133,66 @@ class MessageListActivity : AppCompatActivity() {
getString(R.string.gardner_second),
false,
System.currentTimeMillis()
))
horizontalLayout.visibility = View.GONE
), false)
}
getString(R.string.can_you_say_sth_different) -> {
receiveMessages(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())))
horizontalLayout.visibility = View.GONE
Message(getString(R.string.its_funny), false, System.currentTimeMillis())),
false)
}
}
}
private fun initFirstChoice() {
spinner = findViewById(R.id.spinner)
receiveMessage(Message(getString(R.string.greeting), false, System.currentTimeMillis()))
// Create an ArrayAdapter using the string array and a default spinner layout
spinnerOptions.addAll(resources.getStringArray(R.array.components))
adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, spinnerOptions)
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
adapter.setNotifyOnChange(true)
// Apply the adapter to the spinner
spinner.adapter = adapter
val isInitialized = model.initialized.value
if (isInitialized != null && !isInitialized) {
model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.components))
adapter.notifyDataSetChanged()
model.initialized.value = true
receiveMessage(Message(getString(R.string.greeting), false, System.currentTimeMillis()))
}
else {
val spinnerVisible = model.spinnerVisible.value
if (spinnerVisible != null && spinnerVisible) horizontalLayout.visibility = View.VISIBLE
return
}
}
private fun secondChoice() {
model.spinnerOptions.value?.clear()
when (model.component.value) {
Component.PRINTER -> model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.printer_problems))
Component.PC -> model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.pc_problems))
Component.MONITOR -> model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.monitor_problems))
Component.TV -> model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.tv_problems))
Component.ROUTER -> model.spinnerOptions.value?.addAll(resources.getStringArray(R.array.router_problems))
}
adapter.notifyDataSetChanged()
receiveMessages(listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.select_problem), false, System.currentTimeMillis())
))
spinnerOptions.clear()
when (component) {
Component.PRINTER -> spinnerOptions.addAll(resources.getStringArray(R.array.printer_problems))
Component.PC -> spinnerOptions.addAll(resources.getStringArray(R.array.pc_problems))
Component.MONITOR -> spinnerOptions.addAll(resources.getStringArray(R.array.monitor_problems))
Component.TV -> spinnerOptions.addAll(resources.getStringArray(R.array.tv_problems))
Component.ROUTER -> spinnerOptions.addAll(resources.getStringArray(R.array.router_problems))
}
adapter.notifyDataSetChanged()
}
private fun solutionQuestion() {
val format = "component_problem"
val stringName = format.replace("component", component.toString().toLowerCase()).
replace("problem", problem.toString().toLowerCase())
solutions.addAll(getSolutionArray(stringName))
val stringName = format.replace("component", model.component.value.toString().toLowerCase()).
replace("problem", model.problem.value.toString().toLowerCase())
model.solutions.value?.addAll(getSolutionArray(stringName))
val firstSolutionQuestion = getString(R.string.have_you_tried, solutions[0])
nextSolutionIndex++
receiveMessage(Message(firstSolutionQuestion, false, System.currentTimeMillis()))
spinnerOptions.clear()
spinnerOptions.addAll(resources.getStringArray(R.array.binary))
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(resources.getStringArray(R.array.binary))
adapter.notifyDataSetChanged()
receiveMessages(listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(firstSolutionQuestion, false, System.currentTimeMillis())
))
}
private fun triedAnswer(selection: String) {
@ -200,7 +214,7 @@ class MessageListActivity : AppCompatActivity() {
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.did_it_work), false, System.currentTimeMillis())
))
currentSolutionState = State.DID_IT_WORK
model.currentSolutionState.value = State.DID_IT_WORK
}
private fun pleaseTry() {
@ -208,53 +222,60 @@ class MessageListActivity : AppCompatActivity() {
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, solutions[nextSolutionIndex - 1]), 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() {
receiveMessage(Message(getString(R.string.okay), false, System.currentTimeMillis()))
spinnerOptions.clear()
spinnerOptions.add(getString(R.string.can_you_say_sth_different))
model.spinnerOptions.value?.clear()
model.spinnerOptions.value?.add(getString(R.string.can_you_say_sth_different))
adapter.notifyDataSetChanged()
receiveMessage(Message(getString(R.string.okay), false, System.currentTimeMillis()))
}
private fun nextSolutionQuestion() {
receiveMessages(listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.sorry), false, System.currentTimeMillis())
))
if (nextSolutionIndex == solutions.size) {
if (model.nextSolutionIndex.value == model.solutions.value?.size) {
// we exhausted all options
var summary = """
Component: ${component.toString().toLowerCase().capitalize()}
Problem: ${problem.toString().replace('_', ' ').toLowerCase().capitalizeWords()}
Component: ${model.component.value.toString().toLowerCase().capitalize()}
Problem: ${model.problem.value.toString().replace('_', ' ').toLowerCase().capitalizeWords()}
Tried Solutions:
""".trimIndent()
for (solution in solutions) {
val item = "\n - $solution"
summary = summary.plus(item)
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(getString(R.string.who_are_you))
adapter.notifyDataSetChanged()
receiveMessages(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())
))
spinnerOptions.clear()
spinnerOptions.add(getString(R.string.who_are_you))
adapter.notifyDataSetChanged()
}
else {
val solutionQuestion = getString(R.string.have_you_tried, solutions[nextSolutionIndex])
nextSolutionIndex++
currentSolutionState = State.HAVE_YOU_TRIED
receiveMessage(Message(solutionQuestion, false, System.currentTimeMillis()))
spinnerOptions.clear()
spinnerOptions.addAll(resources.getStringArray(R.array.binary))
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(resources.getStringArray(R.array.binary))
adapter.notifyDataSetChanged()
receiveMessages(listOf(
Message(getString(R.string.okay), false, System.currentTimeMillis()),
Message(getString(R.string.sorry), false, System.currentTimeMillis()),
Message(solutionQuestion, false, System.currentTimeMillis())
))
}
}

View File

@ -11,6 +11,26 @@ class MessageListViewModel : ViewModel() {
private val messages: MutableLiveData<ArrayList<Message>> by lazy {
MutableLiveData<ArrayList<Message>>(ArrayList<Message>())
}
val spinnerOptions: MutableLiveData<ArrayList<CharSequence>> by lazy {
MutableLiveData<ArrayList<CharSequence>>(ArrayList<CharSequence>())
}
val solutions: MutableLiveData<ArrayList<CharSequence>> by lazy {
MutableLiveData<ArrayList<CharSequence>>(ArrayList<CharSequence>())
}
val nextSolutionIndex: MutableLiveData<Int> by lazy {
MutableLiveData<Int>(0)
}
val currentSolutionState: MutableLiveData<State> by lazy {
MutableLiveData<State>(State.HAVE_YOU_TRIED)
}
val component: MutableLiveData<Component> by lazy {
MutableLiveData<Component>()
}
val problem: MutableLiveData<Problem> by lazy {
MutableLiveData<Problem>()
}
val initialized: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>(false) }
val spinnerVisible: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>(false) }
private val viewModelJob = SupervisorJob()
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)