|
@@ -11,8 +11,11 @@ import android.view.MotionEvent
|
|
|
import android.view.View
|
|
import android.view.View
|
|
|
import android.view.ViewConfiguration
|
|
import android.view.ViewConfiguration
|
|
|
import android.view.WindowInsets
|
|
import android.view.WindowInsets
|
|
|
|
|
+import android.view.accessibility.AccessibilityEvent
|
|
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
|
import android.widget.FrameLayout
|
|
import android.widget.FrameLayout
|
|
|
|
|
+import android.widget.TextView
|
|
|
|
|
+import androidx.core.view.ViewCompat
|
|
|
import io.legado.app.BuildConfig
|
|
import io.legado.app.BuildConfig
|
|
|
import io.legado.app.R
|
|
import io.legado.app.R
|
|
|
import io.legado.app.constant.PageAnim
|
|
import io.legado.app.constant.PageAnim
|
|
@@ -118,6 +121,7 @@ class ReadView(
|
|
|
private val upProgressThrottle = throttle(200) { post { upProgress() } }
|
|
private val upProgressThrottle = throttle(200) { post { upProgress() } }
|
|
|
val autoPager = AutoPager(this)
|
|
val autoPager = AutoPager(this)
|
|
|
val isAutoPage get() = autoPager.isRunning
|
|
val isAutoPage get() = autoPager.isRunning
|
|
|
|
|
+ private var accessibilityPageText = ""
|
|
|
|
|
|
|
|
init {
|
|
init {
|
|
|
if (!isInEditMode) {
|
|
if (!isInEditMode) {
|
|
@@ -130,10 +134,16 @@ class ReadView(
|
|
|
addView(nextPage)
|
|
addView(nextPage)
|
|
|
addView(curPage)
|
|
addView(curPage)
|
|
|
addView(prevPage)
|
|
addView(prevPage)
|
|
|
|
|
+ // 三个 PageView 只负责把同一页绘制到 Canvas;它们的内部 View 没有正文语义。
|
|
|
|
|
+ // 读屏应只命中下面由 ReadView 提供的当前页文本节点。
|
|
|
|
|
+ listOf(prevPage, curPage, nextPage).forEach {
|
|
|
|
|
+ it.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
|
|
|
|
+ }
|
|
|
descendantFocusability = FOCUS_BLOCK_DESCENDANTS
|
|
descendantFocusability = FOCUS_BLOCK_DESCENDANTS
|
|
|
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
|
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
|
|
isClickable = true
|
|
isClickable = true
|
|
|
isFocusable = true
|
|
isFocusable = true
|
|
|
|
|
+ ViewCompat.setScreenReaderFocusable(this, true)
|
|
|
prevPage.invisible()
|
|
prevPage.invisible()
|
|
|
nextPage.invisible()
|
|
nextPage.invisible()
|
|
|
curPage.markAsMainView()
|
|
curPage.markAsMainView()
|
|
@@ -183,6 +193,9 @@ class ReadView(
|
|
|
return true
|
|
return true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Canvas 正文没有子级语义,触摸探索必须命中本页级节点。
|
|
|
|
|
+ override fun onInterceptHoverEvent(event: MotionEvent): Boolean = true
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 触摸事件
|
|
* 触摸事件
|
|
|
*/
|
|
*/
|
|
@@ -289,7 +302,9 @@ class ReadView(
|
|
|
|
|
|
|
|
override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
|
|
override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
|
|
|
super.onInitializeAccessibilityNodeInfo(info)
|
|
super.onInitializeAccessibilityNodeInfo(info)
|
|
|
- info.className = ReadView::class.java.name
|
|
|
|
|
|
|
+ // The Canvas-rendered page is exposed as one stable text node.
|
|
|
|
|
+ info.className = TextView::class.java.name
|
|
|
|
|
+ info.text = accessibilityPageText
|
|
|
info.isScrollable = true
|
|
info.isScrollable = true
|
|
|
info.addAction(
|
|
info.addAction(
|
|
|
AccessibilityNodeInfo.AccessibilityAction(
|
|
AccessibilityNodeInfo.AccessibilityAction(
|
|
@@ -311,6 +326,13 @@ class ReadView(
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {
|
|
|
|
|
+ super.onInitializeAccessibilityEvent(event)
|
|
|
|
|
+ if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
|
|
|
|
|
+ event.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
|
|
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
|
|
|
return when (action) {
|
|
return when (action) {
|
|
|
AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> {
|
|
AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> {
|
|
@@ -628,7 +650,7 @@ class ReadView(
|
|
|
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
|
|
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
|
|
|
post {
|
|
post {
|
|
|
val text = pageFactory.curPage.text
|
|
val text = pageFactory.curPage.text
|
|
|
- contentDescription = text
|
|
|
|
|
|
|
+ updateAccessibilityPageText(text)
|
|
|
if (BuildConfig.DEBUG) {
|
|
if (BuildConfig.DEBUG) {
|
|
|
// Device-independent readiness/page-change signal for the debug
|
|
// Device-independent readiness/page-change signal for the debug
|
|
|
// scenario runner (uiautomator does not expose ReadView on all
|
|
// scenario runner (uiautomator does not expose ReadView on all
|
|
@@ -661,6 +683,14 @@ class ReadView(
|
|
|
callBack.screenOffTimerStart()
|
|
callBack.screenOffTimerStart()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private fun updateAccessibilityPageText(text: String) {
|
|
|
|
|
+ if (accessibilityPageText == text) return
|
|
|
|
|
+ accessibilityPageText = text
|
|
|
|
|
+ if (isAttachedToWindow) {
|
|
|
|
|
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private fun upProgress() {
|
|
private fun upProgress() {
|
|
|
curPage.setProgress(pageFactory.curPage)
|
|
curPage.setProgress(pageFactory.curPage)
|
|
|
}
|
|
}
|