From: Selim Cinek Date: Wed, 15 Jun 2016 18:46:37 +0000 (-0700) Subject: Added dismiss, expand, and collapse accessibility actions X-Git-Tag: android-x86-7.1-r1~3090^2~240^2~6 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e9bad242f38bebadae481a22b647cc153f093070;p=android-x86%2Fframeworks-base.git Added dismiss, expand, and collapse accessibility actions Fixes: 20343017 Fixes: 29368014 Change-Id: Ib571242aac04c67aea2f3c3ce76139eaedc1f3f1 --- diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 3069e5ade582..c46acae38c2c 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -22,6 +22,7 @@ import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; @@ -63,6 +64,33 @@ public class NotificationHeaderView extends ViewGroup { } } }; + final AccessibilityDelegate mExpandDelegate = new AccessibilityDelegate() { + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (super.performAccessibilityAction(host, action, args)) { + return true; + } + if (action == AccessibilityNodeInfo.ACTION_COLLAPSE + || action == AccessibilityNodeInfo.ACTION_EXPAND) { + mExpandClickListener.onClick(mExpandButton); + return true; + } + return false; + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + // Avoid that the button description is also spoken + info.setClassName(getClass().getName()); + if (mExpanded) { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); + } else { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); + } + } + }; public NotificationHeaderView(Context context) { this(context, null); @@ -92,6 +120,9 @@ public class NotificationHeaderView extends ViewGroup { mAppName = findViewById(com.android.internal.R.id.app_name_text); mHeaderText = findViewById(com.android.internal.R.id.header_text); mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button); + if (mExpandButton != null) { + mExpandButton.setAccessibilityDelegate(mExpandDelegate); + } mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); } @@ -230,7 +261,7 @@ public class NotificationHeaderView extends ViewGroup { public void setOnClickListener(@Nullable OnClickListener l) { mExpandClickListener = l; setOnTouchListener(mExpandClickListener != null ? mTouchListener : null); - setFocusable(l != null); + mExpandButton.setOnClickListener(mExpandClickListener); updateTouchListener(); } @@ -380,19 +411,6 @@ public class NotificationHeaderView extends ViewGroup { return this; } - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - if (mExpandClickListener != null) { - AccessibilityNodeInfo.AccessibilityAction expand - = new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.ACTION_CLICK, - getContext().getString( - com.android.internal.R.string.expand_action_accessibility)); - info.addAction(expand); - } - } - public ImageView getExpandButton() { return mExpandButton; } diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java new file mode 100644 index 000000000000..f4f49b1e4ffe --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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.internal.widget; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.ImageView; +import android.widget.RemoteViews; + +/** + * An expand button in a notification + */ +@RemoteViews.RemoteView +public class NotificationExpandButton extends ImageView { + public NotificationExpandButton(Context context) { + super(context); + } + + public NotificationExpandButton(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public void getBoundsOnScreen(Rect outRect, boolean clipToParent) { + super.getBoundsOnScreen(outRect, clipToParent); + extendRectToMinTouchSize(outRect); + } + + private void extendRectToMinTouchSize(Rect rect) { + int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48); + rect.left = rect.centerX() - touchTargetSize / 2; + rect.right = rect.left + touchTargetSize; + rect.top = rect.centerY() - touchTargetSize / 2; + rect.bottom = rect.top + touchTargetSize; + } +} diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 38ea92a73a5a..38f671c21cbb 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -89,7 +89,7 @@ android:layout="@layout/notification_template_part_chronometer" android:visibility="gone" /> - notificationChildren = + mNotificationParent.getNotificationChildren(); + int i = notificationChildren.indexOf(this); + if (i != -1 && i < notificationChildren.size() - 1) { + mChildAfterViewWhenDismissed = notificationChildren.get(i + 1); + } + } } public boolean isDismissed() { @@ -750,6 +769,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mChildrenContainer; } + public View getChildAfterViewWhenDismissed() { + return mChildAfterViewWhenDismissed; + } + + public View getGroupParentWhenDismissed() { + return mGroupParentWhenDismissed; + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } @@ -1326,8 +1353,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private void updateClearability() { // public versions cannot be dismissed - mVetoButton.setVisibility(isClearable() && (!mShowingPublic - || !mSensitiveHiddenInGeneral) ? View.VISIBLE : View.GONE); + mVetoButton.setVisibility(canViewBeDismissed() ? View.VISIBLE : View.GONE); + } + + private boolean canViewBeDismissed() { + return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral); } public void makeActionsVisibile() { @@ -1568,6 +1598,32 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + if (canViewBeDismissed()) { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS); + } + } + + @Override + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (super.performAccessibilityActionInternal(action, arguments)) { + return true; + } + switch (action) { + case AccessibilityNodeInfo.ACTION_DISMISS: + NotificationStackScrollLayout.performDismiss(this, mGroupManager, + true /* fromAccessibility */); + return true; + } + return false; + } + + public boolean shouldRefocusOnDismiss() { + return mRefocusOnDismiss || isAccessibilityFocused(); + } + public interface OnExpandClickListener { void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index a11263a3b9f2..30ac9cad346f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -29,6 +29,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; +import android.widget.ImageView; import com.android.internal.util.NotificationColorUtil; import com.android.systemui.R; @@ -117,6 +118,7 @@ public class NotificationContentView extends FrameLayout { private PendingIntent mPreviousHeadsUpRemoteInputIntent; private int mContentHeightAtAnimationStart = UNDEFINED; + private boolean mFocusOnVisibilityChange; public NotificationContentView(Context context, AttributeSet attrs) { @@ -395,6 +397,19 @@ public class NotificationContentView extends FrameLayout { } } + private void focusExpandButtonIfNecessary() { + if (mFocusOnVisibilityChange) { + NotificationHeaderView header = getVisibleNotificationHeader(); + if (header != null) { + ImageView expandButton = header.getExpandButton(); + if (expandButton != null) { + expandButton.requestAccessibilityFocus(); + } + } + mFocusOnVisibilityChange = false; + } + } + public void setContentHeight(int contentHeight) { mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight()); selectLayout(mAnimate /* animate */, false /* force */); @@ -584,7 +599,8 @@ public class NotificationContentView extends FrameLayout { updateContentTransformation(); } else { int visibleType = calculateVisibleType(); - if (visibleType != mVisibleType || force) { + boolean changedType = visibleType != mVisibleType; + if (changedType || force) { View visibleView = getViewForVisibleType(visibleType); if (visibleView != null) { visibleView.setVisibility(VISIBLE); @@ -604,6 +620,9 @@ public class NotificationContentView extends FrameLayout { updateViewVisibilities(visibleType); } mVisibleType = visibleType; + if (changedType) { + focusExpandButtonIfNecessary(); + } updateBackgroundColor(animate); } } @@ -1133,4 +1152,8 @@ public class NotificationContentView extends FrameLayout { mContentHeightAtAnimationStart = UNDEFINED; } } + + public void setFocusOnVisibilityChange() { + mFocusOnVisibilityChange = true; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 7c391fbde0d6..43f847c23f81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -769,7 +769,7 @@ public class NotificationStackScrollLayout extends ViewGroup mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey()); } } - performDismiss(v); + performDismiss(v, mGroupManager, false /* fromAccessibility */); mFalsingManager.onNotificationDismissed(); if (mFalsingManager.shouldEnforceBouncer()) { @@ -778,17 +778,18 @@ public class NotificationStackScrollLayout extends ViewGroup } } - private void performDismiss(View v) { + public static void performDismiss(View v, NotificationGroupManager groupManager, + boolean fromAccessibility) { if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; - if (mGroupManager.isOnlyChildInSuppressedGroup(row.getStatusBarNotification())) { + if (groupManager.isOnlyChildInGroup(row.getStatusBarNotification())) { ExpandableNotificationRow groupSummary = - mGroupManager.getLogicalGroupSummary(row.getStatusBarNotification()); + groupManager.getLogicalGroupSummary(row.getStatusBarNotification()); if (groupSummary.isClearable()) { - performDismiss(groupSummary); + performDismiss(groupSummary, groupManager, fromAccessibility); } } - row.setDismissed(true); + row.setDismissed(true, fromAccessibility); } final View veto = v.findViewById(R.id.veto); if (veto != null && veto.getVisibility() != View.GONE) { @@ -2265,6 +2266,27 @@ public class NotificationStackScrollLayout extends ViewGroup // Make sure the clipRect we might have set is removed expandableView.setClipTopAmount(0); + + focusNextViewIfFocused(child); + } + + private void focusNextViewIfFocused(View view) { + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (row.shouldRefocusOnDismiss()) { + View nextView = row.getChildAfterViewWhenDismissed(); + if (nextView == null) { + View groupParentWhenDismissed = row.getGroupParentWhenDismissed(); + nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null + ? groupParentWhenDismissed.getTranslationY() + : view.getTranslationY()); + } + if (nextView != null) { + nextView.requestAccessibilityFocus(); + } + } + } + } private boolean isChildInGroup(View child) {