package net.osmand.access; import android.app.Activity; import android.os.Build; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import net.osmand.plus.OsmandApplication; public class AccessibilityAssistant extends AccessibilityDelegateCompat implements OnPageChangeListener { private final Activity hostActivity; private final OsmandApplication app; private volatile boolean discourageUiUpdates; private volatile boolean eventsLocked; private volatile View focusedView; private SparseArray<View> monitoredPages; private View visiblePage; private int visiblePageId; public AccessibilityAssistant(Activity activity) { hostActivity = activity; app = (OsmandApplication)(activity.getApplication()); discourageUiUpdates = false; eventsLocked = false; focusedView = null; monitoredPages = new SparseArray<>(); visiblePage = null; visiblePageId = 0; } public boolean isUiUpdateDiscouraged() { return discourageUiUpdates && app.accessibilityEnabled(); } public View getFocusedView() { return focusedView; } public void lockEvents() { eventsLocked = true; } public void unlockEvents() { if (!hostActivity.getWindow().getDecorView().post(new Runnable() { @Override public void run() { eventsLocked = false; } })) eventsLocked = false; } public void forgetFocus() { focusedView = null; } public void registerPage(View page, int id) { monitoredPages.put(id, page); if (id == visiblePageId) visiblePage = page; ViewCompat.setAccessibilityDelegate(page, this); } @Override public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) { return ((monitoredPages.indexOfValue(host) < 0) || (host == visiblePage)) && super.onRequestSendAccessibilityEvent(host, child, event); } @Override public void sendAccessibilityEvent(View host, int eventType) { boolean passed = !eventsLocked; if (passed) super.sendAccessibilityEvent(host, eventType); notifyEvent(host, eventType, passed); } @Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { boolean passed = !eventsLocked; int eventType = event.getEventType(); if (passed) super.sendAccessibilityEventUnchecked(host, event); notifyEvent(host, eventType, passed); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { visiblePageId = position; visiblePage = monitoredPages.get(position); } @Override public void onPageScrollStateChanged(int state) { } private void processFocusChange(View view, boolean isFocused, boolean eventPassed) { if (view.isClickable() && ((view instanceof ImageView) || (view instanceof ImageButton) || (view instanceof Button))) { discourageUiUpdates = isFocused; } else if (eventPassed || (Build.VERSION.SDK_INT != 17)) { focusedView = isFocused ? view : null; } } private void notifyEvent(View view, int eventType, boolean passed) { if (Build.VERSION.SDK_INT >= 16) { switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: processFocusChange(view, true, passed); break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: processFocusChange(view, false, passed); break; default: break; } } } }