Improve TouchExplorationListener

Add some unit tests (not many are possible due to the internal use of
AccessibilityManager which cannot be mocked).

Test: Existing tests and new ones
Bug: 314729896
Change-Id: I685d8958517d56f53abe9dead7bcf55412abefea
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/TouchExplorationStateProviderTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/TouchExplorationStateProviderTest.kt
new file mode 100644
index 0000000..5cc9727
--- /dev/null
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/TouchExplorationStateProviderTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
+import androidx.compose.runtime.State
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchExplorationStateProviderTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun returns_correct_values() {
+        lateinit var accessibilityManager: AccessibilityManager
+        lateinit var accessibilityStatus: State<Boolean>
+        rule.setContent {
+            val context = LocalContext.current
+            accessibilityManager =
+                context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+
+            val touchExplorationStateProvider = DefaultTouchExplorationStateProvider()
+            accessibilityStatus = touchExplorationStateProvider.touchExplorationState()
+        }
+        assertEquals(
+            accessibilityStatus.value,
+            accessibilityManager.isEnabled && accessibilityManager.isTouchExplorationEnabled
+        )
+    }
+}
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/TouchExplorationStateProvider.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/TouchExplorationStateProvider.kt
index 883cf6e2..7e3e7444 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/TouchExplorationStateProvider.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/TouchExplorationStateProvider.kt
@@ -61,16 +61,20 @@
             context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
         }
 
-        val listener = remember { Listener() }
+        val listener = remember { Listener(accessibilityManager) }
 
         LocalLifecycleOwner.current.lifecycle.ObserveState(
             handleEvent = { event ->
                 if (event == Lifecycle.Event.ON_RESUME) {
-                    listener.register(accessibilityManager)
+                    listener.register()
+                } else if (event == Lifecycle.Event.ON_PAUSE) {
+                    listener.unregister()
                 }
             },
             onDispose = {
-                listener.unregister(accessibilityManager)
+                // Unregister the listener in case the PAUSE lifecycle event never came through
+                // Unregistering multiple times is safe
+                listener.unregister()
             }
         )
 
@@ -94,16 +98,18 @@
         }
     }
 
-    private class Listener : AccessibilityStateChangeListener, TouchExplorationStateChangeListener,
-        State<Boolean> {
-        private var accessibilityEnabled by mutableStateOf(false)
-        private var touchExplorationEnabled by mutableStateOf(false)
+    private class Listener constructor(
+        private val accessibilityManager: AccessibilityManager,
+    ) : AccessibilityStateChangeListener, TouchExplorationStateChangeListener, State<Boolean> {
+
+        private var accessibilityEnabled by mutableStateOf(accessibilityManager.isEnabled)
+        private var touchExplorationEnabled by mutableStateOf(
+            accessibilityManager.isTouchExplorationEnabled
+        )
 
         override val value: Boolean
             get() = accessibilityEnabled && touchExplorationEnabled
 
-        fun isEnabled() = accessibilityEnabled && touchExplorationEnabled
-
         override fun onAccessibilityStateChanged(it: Boolean) {
             accessibilityEnabled = it
         }
@@ -112,17 +118,17 @@
             touchExplorationEnabled = it
         }
 
-        fun register(am: AccessibilityManager) {
-            accessibilityEnabled = am.isEnabled
-            touchExplorationEnabled = am.isTouchExplorationEnabled
+        fun register() {
+            accessibilityEnabled = accessibilityManager.isEnabled
+            touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
 
-            am.addTouchExplorationStateChangeListener(this)
-            am.addAccessibilityStateChangeListener(this)
+            accessibilityManager.addTouchExplorationStateChangeListener(this)
+            accessibilityManager.addAccessibilityStateChangeListener(this)
         }
 
-        fun unregister(am: AccessibilityManager) {
-            am.removeTouchExplorationStateChangeListener(this)
-            am.removeAccessibilityStateChangeListener(this)
+        fun unregister() {
+            accessibilityManager.removeTouchExplorationStateChangeListener(this)
+            accessibilityManager.removeAccessibilityStateChangeListener(this)
         }
     }
 }