From 7d09277f99ca919db70d45425bd366ff1b1b7e0e Mon Sep 17 00:00:00 2001 From: Mike Digman Date: Thu, 11 Jan 2018 12:10:32 -0800 Subject: Add rotate suggestion button by default to left side of navbar Icons, animations are preliminary Test: manual Change-Id: If8a6942c1e78f6cfb5aae6d78e6acfa2b0bb6566 --- .../res/drawable/ic_sysbar_rotate_button.xml | 117 ++++++++++++++++ packages/SystemUI/res/layout/rotate_suggestion.xml | 32 +++++ .../systemui/statusbar/phone/ButtonDispatcher.java | 4 + .../statusbar/phone/NavigationBarFragment.java | 155 +++++++++++++++++++++ .../statusbar/phone/NavigationBarInflaterView.java | 5 +- .../statusbar/phone/NavigationBarView.java | 22 +++ .../statusbar/policy/KeyButtonDrawable.java | 2 +- .../statusbar/policy/TintedKeyButtonDrawable.java | 57 ++++++++ 8 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml create mode 100644 packages/SystemUI/res/layout/rotate_suggestion.xml create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml new file mode 100644 index 000000000000..255e377cf9dd --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml new file mode 100644 index 000000000000..7762950b5a23 --- /dev/null +++ b/packages/SystemUI/res/layout/rotate_suggestion.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java index a83e6591c48b..78ee04048644 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java @@ -89,6 +89,10 @@ public class ButtonDispatcher { return mAlpha != null ? mAlpha : 1; } + public KeyButtonDrawable getImageDrawable() { + return mImageDrawable; + } + public void setImageDrawable(KeyButtonDrawable drawable) { mImageDrawable = drawable; final int N = mViews.size(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 695168e3dbc2..70ec45e4cdb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -24,6 +24,10 @@ import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; 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; import android.app.ActivityManagerNative; @@ -39,6 +43,7 @@ import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; import android.inputmethodservice.InputMethodService; import android.os.Binder; import android.os.Bundle; @@ -70,17 +75,22 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; import com.android.systemui.Dependency; import com.android.systemui.OverviewProxyService; +import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.assist.AssistManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.recents.Recents; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.KeyButtonDrawable; import com.android.systemui.statusbar.policy.KeyButtonView; +import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.stack.StackStateAnimator; import java.io.FileDescriptor; @@ -101,6 +111,8 @@ 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; + protected NavigationBarView mNavigationBarView = null; protected AssistManager mAssistManager; @@ -130,6 +142,15 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public boolean mHomeBlockedThisTouch; + private int mLastRotationSuggestion; + private RotationLockController mRotationLockController; + private TaskStackListenerImpl mTaskStackListener; + + private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false); + private Animator mRotateShowAnimator; + private Animator mRotateHideAnimator; + + // ----- Fragment Lifecycle Callbacks ----- @Override @@ -163,6 +184,12 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } + + mRotationLockController = Dependency.get(RotationLockController.class); + + // Register the task stack listener + mTaskStackListener = new TaskStackListenerImpl(); + ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); } @Override @@ -178,6 +205,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } + + // Unregister the task stack listener + ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener); } @Override @@ -304,6 +334,92 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } } + @Override + public void onRotationProposal(final int rotation) { + // This method will only be called if rotation is valid but will include proposals for the + // current system rotation + Handler h = getView().getHandler(); + if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { + // Use this as a signal to remove any current suggestions + h.removeCallbacks(mRemoveRotationProposal); + setRotateSuggestionButtonState(false); + } else { + mLastRotationSuggestion = rotation; // Remember rotation for click + setRotateSuggestionButtonState(true); + h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal + h.postDelayed(mRemoveRotationProposal, + ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout + } + } + + public void setRotateSuggestionButtonState(final boolean visible) { + setRotateSuggestionButtonState(visible, false); + } + + public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) { + ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton(); + 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(); + if (currentView == null) return; + + KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable(); + if (kbd == null) return; + + AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); + if (visible) { // Appear and change + rotBtn.setVisibility(View.VISIBLE); + + if (skipAnim) { + currentView.setAlpha(1f); + return; + } + + // Start a new animation if running + if (mRotateShowAnimator != null) mRotateShowAnimator.pause(); + if (mRotateHideAnimator != null) mRotateHideAnimator.pause(); + + ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha", + 0f, 1f); + appearFade.setDuration(100); + appearFade.setInterpolator(Interpolators.LINEAR); + mRotateShowAnimator = appearFade; + appearFade.start(); + + // Run the rotate icon's animation + animIcon.reset(); + animIcon.start(); + } else { // Hide + + if (skipAnim) { + rotBtn.setVisibility(View.INVISIBLE); + return; + } + + // Don't start any new hide animations if one is running + if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return; + // Pause any active show animations but don't reset the AVD to avoid jumps + if (mRotateShowAnimator != null) mRotateShowAnimator.pause(); + + ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha", + 0f); + fadeOut.setDuration(100); + fadeOut.setInterpolator(Interpolators.LINEAR); + fadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + rotBtn.setVisibility(View.INVISIBLE); + } + }); + + mRotateHideAnimator = fadeOut; + fadeOut.start(); + } + } + // Injected from StatusBar at creation. public void setCurrentSysuiVisibility(int systemUiVisibility) { mSystemUiVisibility = systemUiVisibility; @@ -406,6 +522,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); updateAccessibilityServicesState(mAccessibilityManager); + + ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); + rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); } private boolean onHomeTouch(View v, MotionEvent event) { @@ -598,6 +717,10 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); } + private void onRotateSuggestionClick(View v) { + mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion); + } + // ----- Methods that StatusBar talks to (should be minimized) ----- public void setLightBarController(LightBarController lightBarController) { @@ -646,6 +769,13 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private final Stub mRotationWatcher = new Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { + // If the screen rotation changes while locked, update lock rotation to flow with + // new screen rotation and hide any showing suggestions. + if (mRotationLockController.isRotationLocked()) { + mRotationLockController.setRotationLockedAtAngle(true, rotation); + 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(); @@ -671,6 +801,31 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } }; + class TaskStackListenerImpl extends SysUiTaskStackChangeListener { + // Invalidate any rotation suggestion on task change or activity orientation change + // Note: all callbacks happen on main thread + + @Override + public void onTaskStackChanged() { + setRotateSuggestionButtonState(false); + } + + @Override + public void onTaskRemoved(int taskId) { + setRotateSuggestionButtonState(false); + } + + @Override + public void onTaskMovedToFront(int taskId) { + setRotateSuggestionButtonState(false); + } + + @Override + public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { + setRotateSuggestionButtonState(false); + } + } + public static View create(Context context, FragmentListener listener) { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index 4e79314bb818..b8b309b38b27 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -63,6 +63,7 @@ public class NavigationBarInflaterView extends FrameLayout public static final String RECENT = "recent"; public static final String NAVSPACE = "space"; public static final String CLIPBOARD = "clipboard"; + public static final String ROTATE = "rotate"; public static final String KEY = "key"; public static final String LEFT = "left"; public static final String RIGHT = "right"; @@ -311,7 +312,7 @@ public class NavigationBarInflaterView extends FrameLayout View v = null; String button = extractButton(buttonSpec); if (LEFT.equals(button)) { - String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE); + String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, ROTATE); button = extractButton(s); } else if (RIGHT.equals(button)) { String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME); @@ -334,6 +335,8 @@ public class NavigationBarInflaterView extends FrameLayout v = inflater.inflate(R.layout.nav_key_space, parent, false); } else if (CLIPBOARD.equals(button)) { v = inflater.inflate(R.layout.clipboard, parent, false); + } else if (ROTATE.equals(button)) { + v = inflater.inflate(R.layout.rotate_suggestion, parent, false); } else if (button.startsWith(KEY)) { String uri = extractImage(button); int code = extractKeycode(button); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 392581de8607..006a85b7ef8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.os.RemoteException; +import android.support.annotation.ColorInt; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -56,6 +57,7 @@ import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.phone.NavGesture; import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -94,6 +96,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener