Skip to content

Commit

Permalink
Merge pull request #108 from NeoA11y/refactor/improve-type-class
Browse files Browse the repository at this point in the history
Proposal to refactor Reader and Type classes
  • Loading branch information
Irineu333 authored Nov 30, 2023
2 parents 3bb9d58 + c09dccb commit 840f63f
Show file tree
Hide file tree
Showing 13 changed files with 427 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import android.content.Context
import android.speech.tts.TextToSpeech
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import com.neo.speaktouch.utils.Reader
import com.neo.speaktouch.model.UiText
import com.neo.speaktouch.utils.extension.getLog
import com.neo.speaktouch.model.Text
import timber.log.Timber

class SpeechController(
Expand All @@ -45,17 +44,12 @@ class SpeechController(
)
}

fun speak(text: UiText) {
Timber.i("speak: $text")

fun speak(text: Text) {
speak(text.resolved(context))
}

fun speak(nodeInfo: AccessibilityNodeInfoCompat) {

Timber.i("speak: ${nodeInfo.getLog()}")

speak(reader.getContent(nodeInfo))
speak(reader.read(nodeInfo))
}

fun stop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.neo.speaktouch.controller.Controllers
import com.neo.speaktouch.controller.SpeechController
import com.neo.speaktouch.controller.VibratorController
import com.neo.speaktouch.utils.Reader
import com.neo.speaktouch.model.UiText
import com.neo.speaktouch.model.Text
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -63,7 +63,7 @@ object ControllerModule {
val textToSpeech = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
Controllers.speech.speak(
UiText(R.string.speak_touch_activated)
Text(R.string.speak_touch_activated)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ package com.neo.speaktouch.model
import android.content.Context
import androidx.annotation.StringRes

sealed interface UiText {
sealed interface Text {

val args: List<Any>

data class Raw(
val text: String,
override val args: List<Any>
) : UiText
) : Text

data class Res(
@StringRes val res: Int,
override val args: List<Any>
) : UiText
) : Text

fun resolved(context: Context): String {
val text = when (this) {
Expand All @@ -45,16 +45,15 @@ sealed interface UiText {
return text
}

val args = resolvedArgs(context)
.toTypedArray()
val args = resolvedArgs(context).toTypedArray()

return String.format(text, *args)
}

fun resolvedArgs(context: Context): List<Any> {
return args.map { arg ->
when (arg) {
is UiText -> {
is Text -> {
arg.resolved(context)
}

Expand Down
118 changes: 77 additions & 41 deletions app/src/main/java/com/neo/speaktouch/model/Type.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package com.neo.speaktouch.model

import android.widget.AbsListView
import android.widget.AbsSpinner
import android.widget.Button
import android.widget.CheckBox
import android.widget.CheckedTextView
import android.widget.EditText
Expand All @@ -30,76 +29,113 @@ import android.widget.RadioButton
import android.widget.Switch
import android.widget.ToggleButton
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import com.neo.speaktouch.R
import com.neo.speaktouch.utils.extension.`is`

enum class Type {
NONE,
IMAGE,
SWITCH,
TOGGLE,
RADIO,
CHECKBOX,
BUTTON,
EDITFIELD,
OPTIONS,
LIST,
TITLE,
CHECKEDTEXT;
sealed class Type {

object Image : Type()

sealed class Checkable : Type() {
object Switch : Checkable()

object Toggle : Checkable()

object Radio : Checkable()

object Checkbox : Checkable()

object TextView : Checkable()

object Custom : Checkable()
}

object Button : Type()

object EditField : Type()

object DropdownList : Type()

object List : Type()

object Heading : Type()

companion object {
fun get(node: AccessibilityNodeInfoCompat): Type {
val className = node.className ?: return NONE
fun get(node: AccessibilityNodeInfoCompat): Type? {

val className = node.className ?: return null

/* ImageView */

// View->ImageView->ImageButton
if (className `is` ImageButton::class.java) return BUTTON
// View -> ImageView -> ImageButton
if (className `is` ImageButton::class.java) return Button

// View->ImageView
// View -> ImageView
if (className `is` ImageView::class.java) {
return if (node.isClickable) BUTTON else IMAGE
return if (node.isClickable) Button else Image
}

/* TextView */

// View->TextView->EditText
if (className `is` EditText::class.java) return EDITFIELD
// View -> TextView -> EditText
if (className `is` EditText::class.java) return EditField

// TODO: consider checking node.isEditable

// View->TextView->CheckedTextView
if (className `is` CheckedTextView::class.java) return CHECKEDTEXT
// View -> TextView -> CheckedTextView
if (className `is` CheckedTextView::class.java) return Checkable.TextView

/* Button */

// View->TextView->Button->CompoundButton->Switch
if (className `is` Switch::class.java) return SWITCH
// View -> TextView -> Button -> CompoundButton -> Switch
if (className `is` Switch::class.java) return Checkable.Switch

// View->TextView->Button->CompoundButton->ToggleButton
if (className `is` ToggleButton::class.java) return TOGGLE
// View -> TextView -> Button -> CompoundButton -> ToggleButton
if (className `is` ToggleButton::class.java) return Checkable.Toggle

// View->TextView->Button->CompoundButton->RadioButton
if (className `is` RadioButton::class.java) return RADIO
// View -> TextView -> Button -> CompoundButton -> RadioButton
if (className `is` RadioButton::class.java) return Checkable.Radio

// View->TextView->Button->CompoundButton->CheckBox
if (className `is` CheckBox::class.java) return CHECKBOX
// View -> TextView -> Button -> CompoundButton -> CheckBox
if (className `is` CheckBox::class.java) return Checkable.Checkbox

// View->TextView->Button
if (className `is` Button::class.java) return BUTTON
if (node.isCheckable) return Checkable.Custom

// View -> TextView -> Button
if (className `is` android.widget.Button::class.java) return Button

/* AdapterView */

// View->ViewGroup->AdapterView->AbsListView
if (className `is` AbsListView::class.java) return LIST
// View -> ViewGroup -> AdapterView -> AbsListView
if (className `is` AbsListView::class.java) return List

// View->ViewGroup->AdapterView->AbsSpinner
if (className `is` AbsSpinner::class.java) return OPTIONS
// View -> ViewGroup -> AdapterView -> AbsSpinner
if (className `is` AbsSpinner::class.java) return DropdownList

/* Independent of inheritance */

if (node.collectionInfo != null) return LIST
if (node.collectionInfo != null) return List

if (node.isHeading) return TITLE
// Different behavior from that of TalkBack,
// which allows the combination of other types with heading
if (node.isHeading) return Heading

return NONE
return null
}
}
}

fun Type.toTypeText() = when (this) {
Type.Button -> Text(R.string.text_button_type)
Type.DropdownList -> Text(R.string.text_options_type)
Type.EditField -> Text(R.string.text_editfield_type)
Type.Image -> Text(R.string.text_image_type)
Type.List -> Text(R.string.text_list_type)
Type.Heading -> Text(R.string.text_title_type)
Type.Checkable.Checkbox -> Text(R.string.text_checkbox_type)
Type.Checkable.Radio -> Text(R.string.text_radio_type)
Type.Checkable.Switch -> Text(R.string.text_switch_type)
Type.Checkable.Toggle -> Text(R.string.text_toggle_type)
Type.Checkable.TextView -> null /* don't speak type */
Type.Checkable.Custom -> null /* don't speak type */
}
8 changes: 5 additions & 3 deletions app/src/main/java/com/neo/speaktouch/utils/NodeValidator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ object NodeValidator {
*/
private fun hasTextToRead(node: AccessibilityNodeInfoCompat): Boolean {

// TODO: Replace with node.getContent().isNotNullOrEmpty()
return listOf(
node.text,
node.hintText,
node.contentDescription
node.hintText?.takeIf { node.isEditable },
node.contentDescription?.takeIf { !node.isEditable }
).any { it.isNotNullOrEmpty() }
}

Expand Down Expand Up @@ -106,7 +107,8 @@ object NodeValidator {

if (node.isCheckable) return true

if (node.isEditable) return true // TODO: Consider removing after issue #88
// TODO: Consider removing after issue #88
if (node.isEditable) return true

return hasTextToRead(node)
}
Expand Down
Loading

0 comments on commit 840f63f

Please sign in to comment.