Loading packages/SystemUI/res/layout/rotate_suggestion.xml +9 −15 Original line number Diff line number Diff line Loading @@ -14,19 +14,13 @@ limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="@dimen/navigation_side_padding" android:layout_height="match_parent" android:layout_weight="0" > <com.android.systemui.statusbar.policy.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rotate_suggestion" android:layout_width="@dimen/navigation_extra_key_width" android:layout_height="match_parent" android:layout_marginEnd="2dp" android:visibility="invisible" android:scaleType="centerInside" android:contentDescription="@string/accessibility_rotate_button" /> <!-- TODO android:contentDescription --> </FrameLayout> packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,8 @@ <string name="accessibility_menu">Menu</string> <!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_accessibility_button">Accessibility</string> <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_rotate_button">Rotate screen</string> <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_recent">Overview</string> <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] --> Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +10 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ public class ButtonDispatcher { private View.OnClickListener mClickListener; private View.OnTouchListener mTouchListener; private View.OnLongClickListener mLongClickListener; private View.OnHoverListener mOnHoverListener; private Boolean mLongClickable; private Integer mAlpha; private Float mDarkIntensity; Loading @@ -56,6 +57,7 @@ public class ButtonDispatcher { view.setOnClickListener(mClickListener); view.setOnTouchListener(mTouchListener); view.setOnLongClickListener(mLongClickListener); view.setOnHoverListener(mOnHoverListener); if (mLongClickable != null) { view.setLongClickable(mLongClickable); } Loading Loading @@ -174,6 +176,14 @@ public class ButtonDispatcher { } } public void setOnHoverListener(View.OnHoverListener hoverListener) { mOnHoverListener = hoverListener; final int N = mViews.size(); for (int i = 0; i < N; i++) { mViews.get(i).setOnHoverListener(mOnHoverListener); } } public void setClickable(boolean clickable) { abortCurrentGesture(); final int N = mViews.size(); Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +74 −24 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.app.ActivityManager; Loading Loading @@ -111,7 +110,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000; private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; protected NavigationBarView mNavigationBarView = null; protected AssistManager mAssistManager; Loading @@ -120,6 +119,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private int mNavigationIconHints = 0; private int mNavigationBarMode; private boolean mAccessibilityFeedbackEnabled; private AccessibilityManager mAccessibilityManager; private MagnificationContentObserver mMagnificationObserver; private ContentResolver mContentResolver; Loading @@ -143,6 +143,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public boolean mHomeBlockedThisTouch; private int mLastRotationSuggestion; private boolean mHoveringRotationSuggestion; private RotationLockController mRotationLockController; private TaskStackListenerImpl mTaskStackListener; Loading Loading @@ -345,18 +346,40 @@ public class NavigationBarFragment extends Fragment implements Callbacks { return; } Handler h = getView().getHandler(); if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { // Use this as a signal to remove any current suggestions h.removeCallbacks(mRemoveRotationProposal); getView().getHandler().removeCallbacks(mRemoveRotationProposal); setRotateSuggestionButtonState(false); } else { mLastRotationSuggestion = rotation; // Remember rotation for click setRotateSuggestionButtonState(true); rescheduleRotationTimeout(false); } } private void rescheduleRotationTimeout(final boolean reasonHover) { // May be called due to a new rotation proposal or a change in hover state if (reasonHover) { // Don't reschedule if a hide animator is running if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { return; } // Don't reschedule if not visible if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) { return; } } Handler h = getView().getHandler(); h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal h.postDelayed(mRemoveRotationProposal, ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout computeRotationProposalTimeout()); // Schedule timeout } private int computeRotationProposalTimeout() { if (mAccessibilityFeedbackEnabled) return 20000; if (mHoveringRotationSuggestion) return 16000; return 6000; } public void setRotateSuggestionButtonState(final boolean visible) { Loading @@ -365,20 +388,25 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) { ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton(); boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; // Rerun a show animation to indicate change but don't rerun a hide animation if (!visible && !currentlyVisible) return; View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView(); View currentView = rotBtn.getCurrentView(); if (currentView == null) return; KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable(); KeyButtonDrawable kbd = rotBtn.getImageDrawable(); if (kbd == null) return; AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); AnimatedVectorDrawable animIcon = null; if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) { animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); } if (visible) { // Appear and change rotBtn.setVisibility(View.VISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); if (skipAnim) { currentView.setAlpha(1f); Loading @@ -391,18 +419,22 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha", 0f, 1f); appearFade.setDuration(100); appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); appearFade.setInterpolator(Interpolators.LINEAR); mRotateShowAnimator = appearFade; appearFade.start(); // Run the rotate icon's animation // Run the rotate icon's animation if it has one if (animIcon != null) { animIcon.reset(); animIcon.start(); } } else { // Hide if (skipAnim) { rotBtn.setVisibility(View.INVISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); return; } Loading @@ -413,12 +445,13 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha", 0f); fadeOut.setDuration(100); fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); fadeOut.setInterpolator(Interpolators.LINEAR); fadeOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { rotBtn.setVisibility(View.INVISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); } }); Loading Loading @@ -532,6 +565,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover); } private boolean onHomeTouch(View v, MotionEvent event) { Loading Loading @@ -707,6 +741,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } catch (Settings.SettingNotFoundException e) { } boolean feedbackEnabled = false; // AccessibilityManagerService resolves services for the current user since the local // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission final List<AccessibilityServiceInfo> services = Loading @@ -717,8 +752,15 @@ public class NavigationBarFragment extends Fragment implements Callbacks { if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { requestingServices++; } if (info.feedbackType != 0 && info.feedbackType != AccessibilityServiceInfo.FEEDBACK_GENERIC) { feedbackEnabled = true; } } mAccessibilityFeedbackEnabled = feedbackEnabled; final boolean showAccessibilityButton = requestingServices >= 1; final boolean targetSelection = requestingServices >= 2; mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); Loading @@ -728,6 +770,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion); } private boolean onRotateSuggestionHover(View v, MotionEvent event) { final int action = event.getActionMasked(); mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER) || (action == MotionEvent.ACTION_HOVER_MOVE); rescheduleRotationTimeout(true); return false; // Must return false so a11y hover events are dispatched correctly. } // ----- Methods that StatusBar talks to (should be minimized) ----- public void setLightBarController(LightBarController lightBarController) { Loading Loading @@ -775,7 +825,11 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private final Stub mRotationWatcher = new Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { public void onRotationChanged(final int rotation) throws RemoteException { // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. Handler h = getView().getHandler(); Message msg = Message.obtain(h, () -> { // If the screen rotation changes while locked, update lock rotation to flow with // new screen rotation and hide any showing suggestions. if (mRotationLockController.isRotationLocked()) { Loading @@ -783,10 +837,6 @@ public class NavigationBarFragment extends Fragment implements Callbacks { setRotateSuggestionButtonState(false, true); } // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. Handler h = getView().getHandler(); Message msg = Message.obtain(h, () -> { if (mNavigationBarView != null && mNavigationBarView.needsReorient(rotation)) { repositionNavigationBar(); Loading Loading
packages/SystemUI/res/layout/rotate_suggestion.xml +9 −15 Original line number Diff line number Diff line Loading @@ -14,19 +14,13 @@ limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="@dimen/navigation_side_padding" android:layout_height="match_parent" android:layout_weight="0" > <com.android.systemui.statusbar.policy.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rotate_suggestion" android:layout_width="@dimen/navigation_extra_key_width" android:layout_height="match_parent" android:layout_marginEnd="2dp" android:visibility="invisible" android:scaleType="centerInside" android:contentDescription="@string/accessibility_rotate_button" /> <!-- TODO android:contentDescription --> </FrameLayout>
packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,8 @@ <string name="accessibility_menu">Menu</string> <!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_accessibility_button">Accessibility</string> <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_rotate_button">Rotate screen</string> <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_recent">Overview</string> <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] --> Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +10 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ public class ButtonDispatcher { private View.OnClickListener mClickListener; private View.OnTouchListener mTouchListener; private View.OnLongClickListener mLongClickListener; private View.OnHoverListener mOnHoverListener; private Boolean mLongClickable; private Integer mAlpha; private Float mDarkIntensity; Loading @@ -56,6 +57,7 @@ public class ButtonDispatcher { view.setOnClickListener(mClickListener); view.setOnTouchListener(mTouchListener); view.setOnLongClickListener(mLongClickListener); view.setOnHoverListener(mOnHoverListener); if (mLongClickable != null) { view.setLongClickable(mLongClickable); } Loading Loading @@ -174,6 +176,14 @@ public class ButtonDispatcher { } } public void setOnHoverListener(View.OnHoverListener hoverListener) { mOnHoverListener = hoverListener; final int N = mViews.size(); for (int i = 0; i < N; i++) { mViews.get(i).setOnHoverListener(mOnHoverListener); } } public void setClickable(boolean clickable) { abortCurrentGesture(); final int N = mViews.size(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +74 −24 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.app.ActivityManager; Loading Loading @@ -111,7 +110,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000; private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; protected NavigationBarView mNavigationBarView = null; protected AssistManager mAssistManager; Loading @@ -120,6 +119,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private int mNavigationIconHints = 0; private int mNavigationBarMode; private boolean mAccessibilityFeedbackEnabled; private AccessibilityManager mAccessibilityManager; private MagnificationContentObserver mMagnificationObserver; private ContentResolver mContentResolver; Loading @@ -143,6 +143,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public boolean mHomeBlockedThisTouch; private int mLastRotationSuggestion; private boolean mHoveringRotationSuggestion; private RotationLockController mRotationLockController; private TaskStackListenerImpl mTaskStackListener; Loading Loading @@ -345,18 +346,40 @@ public class NavigationBarFragment extends Fragment implements Callbacks { return; } Handler h = getView().getHandler(); if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { // Use this as a signal to remove any current suggestions h.removeCallbacks(mRemoveRotationProposal); getView().getHandler().removeCallbacks(mRemoveRotationProposal); setRotateSuggestionButtonState(false); } else { mLastRotationSuggestion = rotation; // Remember rotation for click setRotateSuggestionButtonState(true); rescheduleRotationTimeout(false); } } private void rescheduleRotationTimeout(final boolean reasonHover) { // May be called due to a new rotation proposal or a change in hover state if (reasonHover) { // Don't reschedule if a hide animator is running if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { return; } // Don't reschedule if not visible if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) { return; } } Handler h = getView().getHandler(); h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal h.postDelayed(mRemoveRotationProposal, ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout computeRotationProposalTimeout()); // Schedule timeout } private int computeRotationProposalTimeout() { if (mAccessibilityFeedbackEnabled) return 20000; if (mHoveringRotationSuggestion) return 16000; return 6000; } public void setRotateSuggestionButtonState(final boolean visible) { Loading @@ -365,20 +388,25 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) { ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton(); boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; // Rerun a show animation to indicate change but don't rerun a hide animation if (!visible && !currentlyVisible) return; View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView(); View currentView = rotBtn.getCurrentView(); if (currentView == null) return; KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable(); KeyButtonDrawable kbd = rotBtn.getImageDrawable(); if (kbd == null) return; AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); AnimatedVectorDrawable animIcon = null; if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) { animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); } if (visible) { // Appear and change rotBtn.setVisibility(View.VISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); if (skipAnim) { currentView.setAlpha(1f); Loading @@ -391,18 +419,22 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha", 0f, 1f); appearFade.setDuration(100); appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); appearFade.setInterpolator(Interpolators.LINEAR); mRotateShowAnimator = appearFade; appearFade.start(); // Run the rotate icon's animation // Run the rotate icon's animation if it has one if (animIcon != null) { animIcon.reset(); animIcon.start(); } } else { // Hide if (skipAnim) { rotBtn.setVisibility(View.INVISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); return; } Loading @@ -413,12 +445,13 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha", 0f); fadeOut.setDuration(100); fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); fadeOut.setInterpolator(Interpolators.LINEAR); fadeOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { rotBtn.setVisibility(View.INVISIBLE); mNavigationBarView.notifyAccessibilitySubtreeChanged(); } }); Loading Loading @@ -532,6 +565,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover); } private boolean onHomeTouch(View v, MotionEvent event) { Loading Loading @@ -707,6 +741,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } catch (Settings.SettingNotFoundException e) { } boolean feedbackEnabled = false; // AccessibilityManagerService resolves services for the current user since the local // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission final List<AccessibilityServiceInfo> services = Loading @@ -717,8 +752,15 @@ public class NavigationBarFragment extends Fragment implements Callbacks { if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { requestingServices++; } if (info.feedbackType != 0 && info.feedbackType != AccessibilityServiceInfo.FEEDBACK_GENERIC) { feedbackEnabled = true; } } mAccessibilityFeedbackEnabled = feedbackEnabled; final boolean showAccessibilityButton = requestingServices >= 1; final boolean targetSelection = requestingServices >= 2; mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); Loading @@ -728,6 +770,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion); } private boolean onRotateSuggestionHover(View v, MotionEvent event) { final int action = event.getActionMasked(); mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER) || (action == MotionEvent.ACTION_HOVER_MOVE); rescheduleRotationTimeout(true); return false; // Must return false so a11y hover events are dispatched correctly. } // ----- Methods that StatusBar talks to (should be minimized) ----- public void setLightBarController(LightBarController lightBarController) { Loading Loading @@ -775,7 +825,11 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private final Stub mRotationWatcher = new Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { public void onRotationChanged(final int rotation) throws RemoteException { // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. Handler h = getView().getHandler(); Message msg = Message.obtain(h, () -> { // If the screen rotation changes while locked, update lock rotation to flow with // new screen rotation and hide any showing suggestions. if (mRotationLockController.isRotationLocked()) { Loading @@ -783,10 +837,6 @@ public class NavigationBarFragment extends Fragment implements Callbacks { setRotateSuggestionButtonState(false, true); } // We need this to be scheduled as early as possible to beat the redrawing of // window in response to the orientation change. Handler h = getView().getHandler(); Message msg = Message.obtain(h, () -> { if (mNavigationBarView != null && mNavigationBarView.needsReorient(rotation)) { repositionNavigationBar(); Loading