Add APIs to add and to remove listeners for
AudioDescriptionStateChange.

Test: atest AccessibilityManagerTest
Bug: 160823970
Change-Id: Ifd5cc3366b4d19b8be20c3fa1ef83437b422bf4b
diff --git a/core/api/current.txt b/core/api/current.txt
index 93f1003..7f93f05 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -51145,6 +51145,7 @@
     method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
     method public boolean addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler);
+    method public void addAudioDescriptionByDefaultStateChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionByDefaultStateChangeListener);
     method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
     method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler);
     method @ColorInt public int getAccessibilityFocusColor();
@@ -51161,6 +51162,7 @@
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
     method public boolean removeAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
+    method public boolean removeAudioDescriptionByDefaultStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionByDefaultStateChangeListener);
     method public boolean removeTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     field public static final int FLAG_CONTENT_CONTROLS = 4; // 0x4
@@ -51176,6 +51178,10 @@
     method public void onAccessibilityStateChanged(boolean);
   }
 
+  public static interface AccessibilityManager.AudioDescriptionByDefaultStateChangeListener {
+    method public void onAudioDescriptionByDefaultStateChanged(boolean);
+  }
+
   public static interface AccessibilityManager.TouchExplorationStateChangeListener {
     method public void onTouchExplorationStateChanged(boolean);
   }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index dc61727..078b767 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -273,6 +273,9 @@
     private final ArrayMap<AccessibilityServicesStateChangeListener, Executor>
             mServicesStateChangeListeners = new ArrayMap<>();
 
+    private final ArrayMap<AudioDescriptionByDefaultStateChangeListener, Executor>
+            mAudioDescriptionByDefaultStateChangeListeners = new ArrayMap<>();
+
     /**
      * Map from a view's accessibility id to the list of request preparers set for that view
      */
@@ -353,6 +356,21 @@
     }
 
     /**
+     * Listener for the audio description by default state. To listen for
+     * changes to the audio description by default state on the device,
+     * implement this interface and register it with the system by calling
+     * {@link #addAudioDescriptionByDefaultStateChangeListener}.
+     */
+    public interface AudioDescriptionByDefaultStateChangeListener {
+        /**
+         * Called when the audio description enabled state changes.
+         *
+         * @param enabled Whether audio description by default is enabled.
+         */
+        void onAudioDescriptionByDefaultStateChanged(boolean enabled);
+    }
+
+    /**
      * Policy to inject behavior into the accessibility manager.
      *
      * @hide
@@ -1159,6 +1177,35 @@
     }
 
     /**
+     * Registers a {@link AudioDescriptionByDefaultStateChangeListener}
+     * for changes in the audio description by default state of the system.
+     * The value could be read via {@link #isAudioDescriptionRequested}.
+     *
+     * @param executor The executor on which the listener should be called back.
+     * @param listener The listener.
+     */
+    public void addAudioDescriptionByDefaultStateChangeListener(
+            @NonNull Executor executor,
+            @NonNull AudioDescriptionByDefaultStateChangeListener listener) {
+        synchronized (mLock) {
+            mAudioDescriptionByDefaultStateChangeListeners.put(listener, executor);
+        }
+    }
+
+    /**
+     * Unregisters a {@link AudioDescriptionByDefaultStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if listener was previously registered.
+     */
+    public boolean removeAudioDescriptionByDefaultStateChangeListener(
+            @NonNull AudioDescriptionByDefaultStateChangeListener listener) {
+        synchronized (mLock) {
+            return (mAudioDescriptionByDefaultStateChangeListeners.remove(listener) != null);
+        }
+    }
+
+    /**
      * Sets the {@link AccessibilityPolicy} controlling this manager.
      *
      * @param policy The policy.
@@ -1303,7 +1350,7 @@
         final boolean wasEnabled = isEnabled();
         final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
         final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
-
+        final boolean wasAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested;
 
         // Ensure listeners get current state from isZzzEnabled() calls.
         mIsEnabled = enabled;
@@ -1323,6 +1370,11 @@
             notifyHighTextContrastStateChanged();
         }
 
+        if (wasAudioDescriptionByDefaultRequested
+                != audioDescriptionEnabled) {
+            notifyAudioDescriptionbyDefaultStateChanged();
+        }
+
         updateAccessibilityTracingState(stateFlags);
     }
 
@@ -1688,15 +1740,20 @@
 
     /**
      * Determines if users want to select sound track with audio description by default.
-     *
+     * <p>
      * Audio description, also referred to as a video description, described video, or
      * more precisely called a visual description, is a form of narration used to provide
      * information surrounding key visual elements in a media work for the benefit of
      * blind and visually impaired consumers.
-     *
+     * </p>
+     * <p>
      * The method provides the preference value to content provider apps to select the
      * default sound track during playing a video or movie.
-     *
+     * </p>
+     * <p>
+     * Add listener to detect the state change via
+     * {@link #addAudioDescriptionByDefaultStateChangeListener}
+     * </p>
      * @return {@code true} if the audio description is enabled, {@code false} otherwise.
      */
     public boolean isAudioDescriptionRequested() {
@@ -1804,6 +1861,29 @@
     }
 
     /**
+     * Notifies the registered {@link AudioDescriptionStateChangeListener}s.
+     */
+    private void notifyAudioDescriptionbyDefaultStateChanged() {
+        final boolean isAudioDescriptionByDefaultRequested;
+        final ArrayMap<AudioDescriptionByDefaultStateChangeListener, Executor> listeners;
+        synchronized (mLock) {
+            if (mAudioDescriptionByDefaultStateChangeListeners.isEmpty()) {
+                return;
+            }
+            isAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested;
+            listeners = new ArrayMap<>(mAudioDescriptionByDefaultStateChangeListeners);
+        }
+
+        final int numListeners = listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            final AudioDescriptionByDefaultStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).execute(() ->
+                    listener.onAudioDescriptionByDefaultStateChanged(
+                        isAudioDescriptionByDefaultRequested));
+        }
+    }
+
+    /**
      * Update mAccessibilityTracingState.
      */
     private void updateAccessibilityTracingState(int stateFlag) {