Loading src/com/android/documentsui/DropdownBreadcrumb.java +2 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Dropdown implementation of breadcrumb used for phone device layouts Loading Loading @@ -60,7 +60,7 @@ public final class DropdownBreadcrumb extends Spinner implements Breadcrumb { } @Override public void setup(Environment env, State state, Consumer<Integer> listener) { public void setup(Environment env, State state, IntConsumer listener) { mAdapter = new DropdownAdapter(state, env); setOnItemSelectedListener( new OnItemSelectedListener() { Loading src/com/android/documentsui/HorizontalBreadcrumb.java +23 −5 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ import com.android.documentsui.NavigationViewManager.Breadcrumb; import com.android.documentsui.NavigationViewManager.Environment; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.dirlist.AccessibilityClickEventRouter; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Horizontal implementation of breadcrumb used for tablet / desktop device layouts Loading @@ -44,7 +46,7 @@ public final class HorizontalBreadcrumb extends RecyclerView private LinearLayoutManager mLayoutManager; private BreadcrumbAdapter mAdapter; private Consumer<Integer> mListener; private IntConsumer mClickListener; public HorizontalBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Loading @@ -61,13 +63,20 @@ public final class HorizontalBreadcrumb extends RecyclerView @Override public void setup(Environment env, com.android.documentsui.base.State state, Consumer<Integer> listener) { IntConsumer listener) { mListener = listener; mClickListener = listener; mLayoutManager = new LinearLayoutManager( getContext(), LinearLayoutManager.HORIZONTAL, false); mAdapter = new BreadcrumbAdapter( state, env, new ItemDragListener<>(this)); // Since we are using GestureDetector to detect click events, a11y services don't know which views // are clickable because we aren't using View.OnClickListener. Thus, we need to use a custom // accessibility delegate to route click events correctly. See AccessibilityClickEventRouter // for more details on how we are routing these a11y events. setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(this, (View child) -> onAccessibilityClick(child))); setLayoutManager(mLayoutManager); addOnItemTouchListener(new ClickListener(getContext(), this::onSingleTapUp)); Loading Loading @@ -108,6 +117,15 @@ public final class HorizontalBreadcrumb extends RecyclerView return (maxOffset - computeHorizontalScrollOffset() > USER_NO_SCROLL_OFFSET_THRESHOLD); } private boolean onAccessibilityClick(View child) { int pos = getChildAdapterPosition(child); if (pos != getAdapter().getItemCount() - 1) { mClickListener.accept(pos); return true; } return false; } @Override public void postUpdate() { } Loading Loading @@ -139,7 +157,7 @@ public final class HorizontalBreadcrumb extends RecyclerView public void onViewHovered(View v) { int pos = getChildAdapterPosition(v); if (pos != mAdapter.getItemCount() - 1) { mListener.accept(pos); mClickListener.accept(pos); } } Loading @@ -147,7 +165,7 @@ public final class HorizontalBreadcrumb extends RecyclerView View itemView = findChildViewUnder(e.getX(), e.getY()); int pos = getChildAdapterPosition(itemView); if (pos != mAdapter.getItemCount() - 1) { mListener.accept(pos); mClickListener.accept(pos); } } Loading src/com/android/documentsui/NavigationViewManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.AnimationView; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * A facade over the portions of the app and drawer toolbars. Loading Loading @@ -124,7 +124,7 @@ public class NavigationViewManager { } interface Breadcrumb { void setup(Environment env, State state, Consumer<Integer> listener); void setup(Environment env, State state, IntConsumer listener); void show(boolean visibility); void postUpdate(); } Loading src/com/android/documentsui/dirlist/AccessibilityClickEventRouter.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 com.android.documentsui.dirlist; import android.os.Bundle; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerViewAccessibilityDelegate; import android.view.View; import java.util.function.Function; /** * Custom Accessibility Delegate for RecyclerViews to route click events on its child views to * proper handlers. * * The majority of event handling is done using TouchDetector instead of View.OnCLickListener, * which most a11y services use to understand whether a particular view is clickable or not. * Thus, we need to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child * views' accessibility node, and then correctly route these clicks done by a11y services to responsible * click callbacks. */ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDelegate { private final ItemDelegate mItemDelegate; private final Function<View, Boolean> mClickCallback; public AccessibilityClickEventRouter( RecyclerView recyclerView, Function<View, Boolean> clickCallback) { super(recyclerView); mClickCallback = clickCallback; mItemDelegate = new ItemDelegate(this) { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityActionCompat.ACTION_CLICK); } @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { // We are only handling click events; route all other to default implementation if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { return mClickCallback.apply(host); } return super.performAccessibilityAction(host, action, args); } }; } @Override public AccessibilityDelegateCompat getItemDelegate() { return mItemDelegate; } } No newline at end of file src/com/android/documentsui/dirlist/DirectoryFragment.java +9 −0 Original line number Diff line number Diff line Loading @@ -305,6 +305,9 @@ public class DirectoryFragment extends Fragment mFocusManager = mInjector.getFocusManager(mRecView, mModel); mActions = mInjector.getActionHandler(mModel); mRecView.setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(mRecView, (View child) -> onAccessibilityClick(child))); mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); Loading Loading @@ -669,6 +672,12 @@ public class DirectoryFragment extends Fragment return false; } private boolean onAccessibilityClick(View child) { DocumentDetails doc = getDocumentHolder(child); mActions.openDocument(doc); return true; } private void cancelThumbnailTask(View view) { final ImageView iconThumb = (ImageView) view.findViewById(R.id.icon_thumb); if (iconThumb != null) { Loading Loading
src/com/android/documentsui/DropdownBreadcrumb.java +2 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Dropdown implementation of breadcrumb used for phone device layouts Loading Loading @@ -60,7 +60,7 @@ public final class DropdownBreadcrumb extends Spinner implements Breadcrumb { } @Override public void setup(Environment env, State state, Consumer<Integer> listener) { public void setup(Environment env, State state, IntConsumer listener) { mAdapter = new DropdownAdapter(state, env); setOnItemSelectedListener( new OnItemSelectedListener() { Loading
src/com/android/documentsui/HorizontalBreadcrumb.java +23 −5 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ import com.android.documentsui.NavigationViewManager.Breadcrumb; import com.android.documentsui.NavigationViewManager.Environment; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.dirlist.AccessibilityClickEventRouter; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Horizontal implementation of breadcrumb used for tablet / desktop device layouts Loading @@ -44,7 +46,7 @@ public final class HorizontalBreadcrumb extends RecyclerView private LinearLayoutManager mLayoutManager; private BreadcrumbAdapter mAdapter; private Consumer<Integer> mListener; private IntConsumer mClickListener; public HorizontalBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Loading @@ -61,13 +63,20 @@ public final class HorizontalBreadcrumb extends RecyclerView @Override public void setup(Environment env, com.android.documentsui.base.State state, Consumer<Integer> listener) { IntConsumer listener) { mListener = listener; mClickListener = listener; mLayoutManager = new LinearLayoutManager( getContext(), LinearLayoutManager.HORIZONTAL, false); mAdapter = new BreadcrumbAdapter( state, env, new ItemDragListener<>(this)); // Since we are using GestureDetector to detect click events, a11y services don't know which views // are clickable because we aren't using View.OnClickListener. Thus, we need to use a custom // accessibility delegate to route click events correctly. See AccessibilityClickEventRouter // for more details on how we are routing these a11y events. setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(this, (View child) -> onAccessibilityClick(child))); setLayoutManager(mLayoutManager); addOnItemTouchListener(new ClickListener(getContext(), this::onSingleTapUp)); Loading Loading @@ -108,6 +117,15 @@ public final class HorizontalBreadcrumb extends RecyclerView return (maxOffset - computeHorizontalScrollOffset() > USER_NO_SCROLL_OFFSET_THRESHOLD); } private boolean onAccessibilityClick(View child) { int pos = getChildAdapterPosition(child); if (pos != getAdapter().getItemCount() - 1) { mClickListener.accept(pos); return true; } return false; } @Override public void postUpdate() { } Loading Loading @@ -139,7 +157,7 @@ public final class HorizontalBreadcrumb extends RecyclerView public void onViewHovered(View v) { int pos = getChildAdapterPosition(v); if (pos != mAdapter.getItemCount() - 1) { mListener.accept(pos); mClickListener.accept(pos); } } Loading @@ -147,7 +165,7 @@ public final class HorizontalBreadcrumb extends RecyclerView View itemView = findChildViewUnder(e.getX(), e.getY()); int pos = getChildAdapterPosition(itemView); if (pos != mAdapter.getItemCount() - 1) { mListener.accept(pos); mClickListener.accept(pos); } } Loading
src/com/android/documentsui/NavigationViewManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.AnimationView; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * A facade over the portions of the app and drawer toolbars. Loading Loading @@ -124,7 +124,7 @@ public class NavigationViewManager { } interface Breadcrumb { void setup(Environment env, State state, Consumer<Integer> listener); void setup(Environment env, State state, IntConsumer listener); void show(boolean visibility); void postUpdate(); } Loading
src/com/android/documentsui/dirlist/AccessibilityClickEventRouter.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 com.android.documentsui.dirlist; import android.os.Bundle; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerViewAccessibilityDelegate; import android.view.View; import java.util.function.Function; /** * Custom Accessibility Delegate for RecyclerViews to route click events on its child views to * proper handlers. * * The majority of event handling is done using TouchDetector instead of View.OnCLickListener, * which most a11y services use to understand whether a particular view is clickable or not. * Thus, we need to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child * views' accessibility node, and then correctly route these clicks done by a11y services to responsible * click callbacks. */ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDelegate { private final ItemDelegate mItemDelegate; private final Function<View, Boolean> mClickCallback; public AccessibilityClickEventRouter( RecyclerView recyclerView, Function<View, Boolean> clickCallback) { super(recyclerView); mClickCallback = clickCallback; mItemDelegate = new ItemDelegate(this) { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityActionCompat.ACTION_CLICK); } @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { // We are only handling click events; route all other to default implementation if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { return mClickCallback.apply(host); } return super.performAccessibilityAction(host, action, args); } }; } @Override public AccessibilityDelegateCompat getItemDelegate() { return mItemDelegate; } } No newline at end of file
src/com/android/documentsui/dirlist/DirectoryFragment.java +9 −0 Original line number Diff line number Diff line Loading @@ -305,6 +305,9 @@ public class DirectoryFragment extends Fragment mFocusManager = mInjector.getFocusManager(mRecView, mModel); mActions = mInjector.getActionHandler(mModel); mRecView.setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(mRecView, (View child) -> onAccessibilityClick(child))); mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); Loading Loading @@ -669,6 +672,12 @@ public class DirectoryFragment extends Fragment return false; } private boolean onAccessibilityClick(View child) { DocumentDetails doc = getDocumentHolder(child); mActions.openDocument(doc); return true; } private void cancelThumbnailTask(View view) { final ImageView iconThumb = (ImageView) view.findViewById(R.id.icon_thumb); if (iconThumb != null) { Loading