From 47e92353f30b02357154babf175c7176e061ade6 Mon Sep 17 00:00:00 2001 From: Tarundeep Singh Date: Mon, 29 Jul 2019 14:19:31 +0530 Subject: Fix: Navigation for menu with Talkback enabled Sending a11y focus event from MenuRow. Bug: 134673950 Change-Id: I577db017165076c1057b6348ebee0a9930adcb0e Test: manual --- src/com/android/tv/menu/ActionCardView.java | 8 ++++ src/com/android/tv/menu/BaseCardView.java | 8 ++++ src/com/android/tv/menu/ItemListRowView.java | 9 ++++ src/com/android/tv/menu/MenuRow.java | 17 +++++++ src/com/android/tv/menu/MenuRowView.java | 16 +++++++ src/com/android/tv/menu/MenuView.java | 59 ++++++++++++------------ src/com/android/tv/menu/PlayControlsButton.java | 8 ++++ src/com/android/tv/menu/PlayControlsRowView.java | 5 ++ 8 files changed, 101 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/com/android/tv/menu/ActionCardView.java b/src/com/android/tv/menu/ActionCardView.java index 3ecd5f5c..0e789c67 100644 --- a/src/com/android/tv/menu/ActionCardView.java +++ b/src/com/android/tv/menu/ActionCardView.java @@ -19,6 +19,7 @@ package com.android.tv.menu; import android.content.Context; import android.util.AttributeSet; import android.util.Log; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; @@ -93,6 +94,13 @@ public class ActionCardView extends RelativeLayout implements ItemListRowView.Ca } } + /** Request focus and accessibility focus on card view. */ + @Override + public boolean requestFocusWithAccessibility() { + return requestFocus() && + performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } + @Override public void onRecycled() {} } diff --git a/src/com/android/tv/menu/BaseCardView.java b/src/com/android/tv/menu/BaseCardView.java index 3a94ebbf..ed78cb73 100644 --- a/src/com/android/tv/menu/BaseCardView.java +++ b/src/com/android/tv/menu/BaseCardView.java @@ -27,6 +27,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.LinearLayout; import android.widget.TextView; import com.android.tv.R; @@ -135,6 +136,13 @@ public abstract class BaseCardView extends LinearLayout implements ItemListRo } } + /** Request focus and accessibility focus on card view. */ + @Override + public boolean requestFocusWithAccessibility() { + return requestFocus() && + performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } + /** Sets text of this card view. */ public void setText(int resId) { if (mTextResId != resId) { diff --git a/src/com/android/tv/menu/ItemListRowView.java b/src/com/android/tv/menu/ItemListRowView.java index 16d5c3eb..cc6d23c3 100644 --- a/src/com/android/tv/menu/ItemListRowView.java +++ b/src/com/android/tv/menu/ItemListRowView.java @@ -44,6 +44,8 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe void onSelected(); void onDeselected(); + + boolean requestFocusWithAccessibility(); } private HorizontalGridView mListView; @@ -114,6 +116,13 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe } } + @Override + protected void requestChildFocus() { + if (mSelectedCard != null) { + mSelectedCard.requestFocusWithAccessibility(); + } + } + public abstract static class ItemListAdapter extends RecyclerView.Adapter { private final MainActivity mMainActivity; diff --git a/src/com/android/tv/menu/MenuRow.java b/src/com/android/tv/menu/MenuRow.java index 8dc12bad..0945a0c5 100644 --- a/src/com/android/tv/menu/MenuRow.java +++ b/src/com/android/tv/menu/MenuRow.java @@ -31,6 +31,8 @@ public abstract class MenuRow { private MenuRowView mMenuRowView; + private boolean mIsReselected = false; + // TODO: Check if the heightResId is really necessary. public MenuRow(Context context, Menu menu, int titleResId, int heightResId) { this(context, menu, context.getString(titleResId), heightResId); @@ -100,4 +102,19 @@ public abstract class MenuRow { public boolean hideTitleWhenSelected() { return false; } + + /** + * Sets if menu row is reselected. + * + * @param isReselected {@code true} if row is reselected; + * else {@code false}. + */ + public void setIsReselected(boolean isReselected) { + mIsReselected = isReselected; + } + + /** Returns true if row is reselected. */ + public boolean isReselected() { + return mIsReselected; + } } diff --git a/src/com/android/tv/menu/MenuRowView.java b/src/com/android/tv/menu/MenuRowView.java index a064f352..e09a4ef0 100644 --- a/src/com/android/tv/menu/MenuRowView.java +++ b/src/com/android/tv/menu/MenuRowView.java @@ -25,6 +25,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.LinearLayout; import android.widget.TextView; import com.android.tv.R; @@ -89,6 +90,18 @@ public abstract class MenuRowView extends LinearLayout { float textSizeDeselected = res.getDimensionPixelSize(R.dimen.menu_row_title_text_size_deselected); mTitleViewScaleSelected = textSizeSelected / textSizeDeselected; + this.setAccessibilityDelegate( + new AccessibilityDelegate() { + @Override + public void sendAccessibilityEvent(View host, int eventType) { + super.sendAccessibilityEvent(host, eventType); + if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED && + !mRow.isReselected()) { + requestChildFocus(); + } + } + } + ); } @Override @@ -177,6 +190,9 @@ public abstract class MenuRowView extends LinearLayout { mLastFocusView = v; } + /** Subclasses should implement this to request focus on child. */ + protected abstract void requestChildFocus(); + /** * Called when the focus of a child view is changed. The inherited class should override this * method instead of calling {@link diff --git a/src/com/android/tv/menu/MenuView.java b/src/com/android/tv/menu/MenuView.java index f5fec000..add4a774 100644 --- a/src/com/android/tv/menu/MenuView.java +++ b/src/com/android/tv/menu/MenuView.java @@ -250,39 +250,40 @@ public class MenuView extends FrameLayout implements IMenuView { // The bounds of the views move and overlap with each other during the animation. In this // situation, the framework can't perform the correct focus navigation. So the menu view // should search by itself. + if (direction == View.FOCUS_UP || direction == View.FOCUS_DOWN) { + return getUpDownFocus(focused, direction); + } + return super.focusSearch(focused, direction); + } + + private View getUpDownFocus(View focused, int direction) { + View newView = super.focusSearch(focused, direction); + MenuRowView oldfocusedParent = getParentMenuRowView(focused); + MenuRowView newFocusedParent = getParentMenuRowView(newView); + int selectedPosition = mLayoutManager.getSelectedPosition(); + int start, delta; if (direction == View.FOCUS_UP) { - View newView = super.focusSearch(focused, direction); - MenuRowView oldfocusedParent = getParentMenuRowView(focused); - MenuRowView newFocusedParent = getParentMenuRowView(newView); - int selectedPosition = mLayoutManager.getSelectedPosition(); - if (newFocusedParent != oldfocusedParent) { - // The focus leaves from the current menu row view. - for (int i = selectedPosition - 1; i >= 0; --i) { - MenuRowView view = mMenuRowViews.get(i); - if (view.getVisibility() == View.VISIBLE) { - return view; - } - } - } - return newView; - } else if (direction == View.FOCUS_DOWN) { - View newView = super.focusSearch(focused, direction); - MenuRowView oldfocusedParent = getParentMenuRowView(focused); - MenuRowView newFocusedParent = getParentMenuRowView(newView); - int selectedPosition = mLayoutManager.getSelectedPosition(); - if (newFocusedParent != oldfocusedParent) { - // The focus leaves from the current menu row view. - int count = mMenuRowViews.size(); - for (int i = selectedPosition + 1; i < count; ++i) { - MenuRowView view = mMenuRowViews.get(i); - if (view.getVisibility() == View.VISIBLE) { - return view; - } + start = selectedPosition - 1; + delta = -1; + } else { + start = selectedPosition + 1; + delta = 1; + } + if (newFocusedParent != oldfocusedParent) { + // The focus leaves from the current menu row view. + int count = mMenuRowViews.size(); + int i = start; + while (i < count && i >= 0) { + MenuRowView view = mMenuRowViews.get(i); + if (view.getVisibility() == View.VISIBLE) { + mMenuRows.get(i).setIsReselected(false); + return view; } + i += delta; } - return newView; } - return super.focusSearch(focused, direction); + mMenuRows.get(selectedPosition).setIsReselected(true); + return newView; } private MenuRowView getParentMenuRowView(View view) { diff --git a/src/com/android/tv/menu/PlayControlsButton.java b/src/com/android/tv/menu/PlayControlsButton.java index ac3292a3..1b85d632 100644 --- a/src/com/android/tv/menu/PlayControlsButton.java +++ b/src/com/android/tv/menu/PlayControlsButton.java @@ -22,6 +22,7 @@ import android.content.Context; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -146,4 +147,11 @@ public class PlayControlsButton extends FrameLayout { mIcon.setAlpha(enabled ? ALPHA_ENABLED : ALPHA_DISABLED); mLabel.setEnabled(enabled); } + + /** Request focus and accessibility focus to the button */ + public boolean requestFocusWithAccessibility() { + return mButton.requestFocus() && + mButton.performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } } diff --git a/src/com/android/tv/menu/PlayControlsRowView.java b/src/com/android/tv/menu/PlayControlsRowView.java index f5e0bcd2..5dde3be0 100644 --- a/src/com/android/tv/menu/PlayControlsRowView.java +++ b/src/com/android/tv/menu/PlayControlsRowView.java @@ -488,6 +488,11 @@ public class PlayControlsRowView extends MenuRowView { } } + @Override + protected void requestChildFocus() { + mPlayPauseButton.requestFocusWithAccessibility(); + } + /** Updates the view contents. It is called from the PlayControlsRow. */ public void update() { updateAll(false); -- cgit v1.2.3