1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.view; 18 19 import android.os.Build; 20 import android.os.Bundle; 21 import android.view.View; 22 import android.view.View.AccessibilityDelegate; 23 import android.view.ViewGroup; 24 import android.view.accessibility.AccessibilityEvent; 25 import android.view.accessibility.AccessibilityNodeInfo; 26 import android.view.accessibility.AccessibilityNodeProvider; 27 28 import androidx.annotation.RequiresApi; 29 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 30 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; 31 32 /** 33 * Helper for accessing {@link AccessibilityDelegate}. 34 * <p> 35 * <strong>Note:</strong> On platform versions prior to 36 * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on 37 * views in the {@code android.widget.*} package are called <i>before</i> 38 * host methods. This prevents certain properties such as class name from 39 * being modified by overriding 40 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}, 41 * as any changes will be overwritten by the host class. 42 * <p> 43 * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate 44 * methods are called <i>after</i> host methods, which all properties to be 45 * modified without being overwritten by the host class. 46 */ 47 public class AccessibilityDelegateCompat { 48 49 private static final class AccessibilityDelegateAdapter extends AccessibilityDelegate { 50 private final AccessibilityDelegateCompat mCompat; 51 AccessibilityDelegateAdapter(AccessibilityDelegateCompat compat)52 AccessibilityDelegateAdapter(AccessibilityDelegateCompat compat) { 53 mCompat = compat; 54 } 55 56 @Override dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event)57 public boolean dispatchPopulateAccessibilityEvent(View host, 58 AccessibilityEvent event) { 59 return mCompat.dispatchPopulateAccessibilityEvent(host, event); 60 } 61 62 @Override onInitializeAccessibilityEvent(View host, AccessibilityEvent event)63 public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 64 mCompat.onInitializeAccessibilityEvent(host, event); 65 } 66 67 @Override onInitializeAccessibilityNodeInfo( View host, AccessibilityNodeInfo info)68 public void onInitializeAccessibilityNodeInfo( 69 View host, AccessibilityNodeInfo info) { 70 mCompat.onInitializeAccessibilityNodeInfo(host, 71 AccessibilityNodeInfoCompat.wrap(info)); 72 } 73 74 @Override onPopulateAccessibilityEvent(View host, AccessibilityEvent event)75 public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 76 mCompat.onPopulateAccessibilityEvent(host, event); 77 } 78 79 @Override onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event)80 public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, 81 AccessibilityEvent event) { 82 return mCompat.onRequestSendAccessibilityEvent(host, child, event); 83 } 84 85 @Override sendAccessibilityEvent(View host, int eventType)86 public void sendAccessibilityEvent(View host, int eventType) { 87 mCompat.sendAccessibilityEvent(host, eventType); 88 } 89 90 @Override sendAccessibilityEventUnchecked(View host, AccessibilityEvent event)91 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { 92 mCompat.sendAccessibilityEventUnchecked(host, event); 93 } 94 95 @Override 96 @RequiresApi(16) getAccessibilityNodeProvider(View host)97 public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) { 98 AccessibilityNodeProviderCompat provider = 99 mCompat.getAccessibilityNodeProvider(host); 100 return (provider != null) 101 ? (AccessibilityNodeProvider) provider.getProvider() : null; 102 } 103 104 @Override performAccessibilityAction(View host, int action, Bundle args)105 public boolean performAccessibilityAction(View host, int action, Bundle args) { 106 return mCompat.performAccessibilityAction(host, action, args); 107 } 108 } 109 110 private static final AccessibilityDelegate DEFAULT_DELEGATE = new AccessibilityDelegate(); 111 112 private final AccessibilityDelegate mBridge; 113 114 /** 115 * Creates a new instance. 116 */ AccessibilityDelegateCompat()117 public AccessibilityDelegateCompat() { 118 mBridge = new AccessibilityDelegateAdapter(this); 119 } 120 121 /** 122 * @return The wrapped bridge implementation. 123 */ getBridge()124 AccessibilityDelegate getBridge() { 125 return mBridge; 126 } 127 128 /** 129 * Sends an accessibility event of the given type. If accessibility is not 130 * enabled this method has no effect. 131 * <p> 132 * The default implementation behaves as {@link View#sendAccessibilityEvent(int) 133 * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate 134 * been set. 135 * </p> 136 * 137 * @param host The View hosting the delegate. 138 * @param eventType The type of the event to send. 139 * 140 * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int) 141 */ sendAccessibilityEvent(View host, int eventType)142 public void sendAccessibilityEvent(View host, int eventType) { 143 DEFAULT_DELEGATE.sendAccessibilityEvent(host, eventType); 144 } 145 146 /** 147 * Sends an accessibility event. This method behaves exactly as 148 * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an 149 * empty {@link AccessibilityEvent} and does not perform a check whether 150 * accessibility is enabled. 151 * <p> 152 * The default implementation behaves as 153 * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent) 154 * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for 155 * the case of no accessibility delegate been set. 156 * </p> 157 * 158 * @param host The View hosting the delegate. 159 * @param event The event to send. 160 * 161 * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent) 162 * View#sendAccessibilityEventUnchecked(AccessibilityEvent) 163 */ sendAccessibilityEventUnchecked(View host, AccessibilityEvent event)164 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { 165 DEFAULT_DELEGATE.sendAccessibilityEventUnchecked(host, event); 166 } 167 168 /** 169 * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then 170 * to its children for adding their text content to the event. 171 * <p> 172 * The default implementation behaves as 173 * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 174 * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for 175 * the case of no accessibility delegate been set. 176 * </p> 177 * 178 * @param host The View hosting the delegate. 179 * @param event The event. 180 * @return True if the event population was completed. 181 * 182 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 183 * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 184 */ dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event)185 public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 186 return DEFAULT_DELEGATE.dispatchPopulateAccessibilityEvent(host, event); 187 } 188 189 /** 190 * Gives a chance to the host View to populate the accessibility event with its 191 * text content. 192 * <p> 193 * The default implementation behaves as 194 * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) 195 * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for 196 * the case of no accessibility delegate been set. 197 * </p> 198 * 199 * @param host The View hosting the delegate. 200 * @param event The accessibility event which to populate. 201 * 202 * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent) 203 * ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) 204 */ onPopulateAccessibilityEvent(View host, AccessibilityEvent event)205 public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 206 DEFAULT_DELEGATE.onPopulateAccessibilityEvent(host, event); 207 } 208 209 /** 210 * Initializes an {@link AccessibilityEvent} with information about the 211 * the host View which is the event source. 212 * <p> 213 * The default implementation behaves as 214 * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event) 215 * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for 216 * the case of no accessibility delegate been set. 217 * </p> 218 * 219 * @param host The View hosting the delegate. 220 * @param event The event to initialize. 221 * 222 * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) 223 * ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) 224 */ onInitializeAccessibilityEvent(View host, AccessibilityEvent event)225 public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 226 DEFAULT_DELEGATE.onInitializeAccessibilityEvent(host, event); 227 } 228 229 /** 230 * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view. 231 * <p> 232 * The default implementation behaves as 233 * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 234 * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for 235 * the case of no accessibility delegate been set. 236 * </p> 237 * 238 * @param host The View hosting the delegate. 239 * @param info The instance to initialize. 240 * 241 * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 242 * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 243 */ onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info)244 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { 245 DEFAULT_DELEGATE.onInitializeAccessibilityNodeInfo( 246 host, info.unwrap()); 247 } 248 249 /** 250 * Called when a child of the host View has requested sending an 251 * {@link AccessibilityEvent} and gives an opportunity to the parent (the host) 252 * to augment the event. 253 * <p> 254 * The default implementation behaves as 255 * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 256 * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for 257 * the case of no accessibility delegate been set. 258 * </p> 259 * 260 * @param host The View hosting the delegate. 261 * @param child The child which requests sending the event. 262 * @param event The event to be sent. 263 * @return True if the event should be sent 264 * 265 * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 266 * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 267 */ onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event)268 public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, 269 AccessibilityEvent event) { 270 return DEFAULT_DELEGATE.onRequestSendAccessibilityEvent(host, child, event); 271 } 272 273 /** 274 * Gets the provider for managing a virtual view hierarchy rooted at this View 275 * and reported to {@link android.accessibilityservice.AccessibilityService}s 276 * that explore the window content. 277 * <p> 278 * The default implementation behaves as 279 * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)} 280 * for the case of no accessibility delegate been set. 281 * </p> 282 * 283 * @return The provider. 284 * 285 * @see AccessibilityNodeProviderCompat 286 */ getAccessibilityNodeProvider(View host)287 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) { 288 if (Build.VERSION.SDK_INT >= 16) { 289 Object provider = DEFAULT_DELEGATE.getAccessibilityNodeProvider(host); 290 if (provider != null) { 291 return new AccessibilityNodeProviderCompat(provider); 292 } 293 } 294 return null; 295 } 296 297 /** 298 * Performs the specified accessibility action on the view. For 299 * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}. 300 * <p> 301 * The default implementation behaves as 302 * {@link View#performAccessibilityAction(int, Bundle) 303 * View#performAccessibilityAction(int, Bundle)} for the case of 304 * no accessibility delegate been set. 305 * </p> 306 * 307 * @param action The action to perform. 308 * @return Whether the action was performed. 309 * 310 * @see View#performAccessibilityAction(int, Bundle) 311 * View#performAccessibilityAction(int, Bundle) 312 */ performAccessibilityAction(View host, int action, Bundle args)313 public boolean performAccessibilityAction(View host, int action, Bundle args) { 314 if (Build.VERSION.SDK_INT >= 16) { 315 return DEFAULT_DELEGATE.performAccessibilityAction(host, action, args); 316 } 317 return false; 318 } 319 } 320