Add rotate suggestion button by default to left side of navbar

Icons, animations are preliminary
Test: manual

Change-Id: If8a6942c1e78f6cfb5aae6d78e6acfa2b0bb6566
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 0000000..255e377
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="24dp"
+                android:width="24dp"
+                android:viewportHeight="102"
+                android:viewportWidth="102"
+                android:tint="?attr/singleToneColor">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_0_G" android:translateX="53.086" android:translateY="48.907000000000004" android:pivotX="-2.083" android:pivotY="2.083" android:rotation="90">
+                    <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:rotation="100.1" android:scaleX="0.7979999999999999" android:scaleY="0.7979999999999999">
+                        <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M17.15 -37.84 C30.19,-31.91 39.52,-19.62 41.9,-4.86 C42.15,-3.39 43.45,-2.31 44.95,-2.31 C46.88,-2.31 48.34,-4.06 48.05,-5.94 C44.37,-27.64 27.64,-45.91 0.84,-48.09 C-1.08,-48.25 -2.17,-45.91 -0.83,-44.53 C-0.83,-44.53 9.87,-33.83 9.87,-33.83 C10.67,-33.04 11.92,-33.04 12.76,-33.79 C12.76,-33.79 17.15,-37.84 17.15,-37.84c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0" android:rotation="87.2" android:scaleX="0.77" android:scaleY="0.77">
+                        <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-21.32 42.01 C-34.36,36.07 -43.68,23.78 -46.07,9.02 C-46.33,7.55 -47.62,6.47 -49.12,6.47 C-51.04,6.47 -52.51,8.23 -52.21,10.11 C-48.53,31.81 -31.81,50.08 -5.01,52.25 C-3.09,52.42 -2,50.08 -3.34,48.7 C-3.34,48.7 -14.04,38 -14.04,38 C-14.84,37.21 -16.11,37.19 -16.93,37.96 C-16.93,37.96 -21.32,42.01 -21.32,42.01c "/>
+                    </group>
+                    <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.77 9.4 C40.77,9.4 -9.4,-40.77 -9.4,-40.77 C-11.91,-43.28 -15.67,-43.28 -18.18,-40.77 C-18.18,-40.77 -44.94,-14.01 -44.94,-14.01 C-47.45,-11.5 -47.45,-7.74 -44.94,-5.23 C-44.94,-5.23 5.23,44.94 5.23,44.94 C7.74,47.45 11.51,47.45 14.01,44.94 C14.01,44.94 40.77,18.18 40.77,18.18 C43.28,15.67 43.28,11.91 40.77,9.4c  M3.85 34.82 C3.85,34.82 -34.4,-3.44 -34.4,-3.44 C-34.4,-3.44 -7.64,-30.19 -7.64,-30.19 C-7.64,-30.19 30.61,8.06 30.61,8.06 C30.61,8.06 3.85,34.82 3.85,34.82c "/>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="100.1" android:valueTo="0" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="87.2" android:valueTo="0" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="90" android:valueTo="0" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX" android:duration="517" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ 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 0000000..7762950
--- /dev/null
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<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
+        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"
+    />
+    <!-- TODO android:contentDescription -->
+</FrameLayout>
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 a83e659..78ee040 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 @@
         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 695168e..70ec45e 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.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.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.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 @@
     /** 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 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 @@
         } 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 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        // Unregister the task stack listener
+        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
     }
 
     @Override
@@ -304,6 +334,92 @@
         }
     }
 
+    @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 @@
         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 @@
         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 @@
     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 @@
         }
     };
 
+    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 4e79314b..b8b309b 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 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 @@
         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 @@
             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 392581d..006a85b 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.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.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 @@
     private KeyButtonDrawable mImeIcon;
     private KeyButtonDrawable mMenuIcon;
     private KeyButtonDrawable mAccessibilityIcon;
+    private KeyButtonDrawable mRotateSuggestionIcon;
 
     private GestureHelper mGestureHelper;
     private DeadZone mDeadZone;
@@ -229,6 +232,9 @@
         mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
         mButtonDispatchers.put(R.id.accessibility_button,
                 new ButtonDispatcher(R.id.accessibility_button));
+        mButtonDispatchers.put(R.id.rotate_suggestion,
+                new ButtonDispatcher(R.id.rotate_suggestion));
+
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
     }
 
@@ -305,6 +311,10 @@
         return mButtonDispatchers.get(R.id.accessibility_button);
     }
 
+    public ButtonDispatcher getRotateSuggestionButton() {
+        return mButtonDispatchers.get(R.id.rotate_suggestion);
+    }
+
     public SparseArray<ButtonDispatcher> getButtonDispatchers() {
         return mButtonDispatchers;
     }
@@ -349,6 +359,11 @@
             mImeIcon = getDrawable(darkContext, lightContext,
                     R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default);
 
+            int lightColor = Utils.getColorAttr(lightContext, R.attr.singleToneColor);
+            int darkColor = Utils.getColorAttr(darkContext, R.attr.singleToneColor);
+            mRotateSuggestionIcon = getDrawable(ctx, R.drawable.ic_sysbar_rotate_button,
+                    lightColor, darkColor);
+
             if (ALTERNATE_CAR_MODE_UI) {
                 updateCarModeIcons(ctx);
             }
@@ -366,6 +381,11 @@
                 darkContext.getDrawable(darkIcon));
     }
 
+    private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon,
+            @ColorInt int lightColor, @ColorInt int darkColor) {
+        return TintedKeyButtonDrawable.create(ctx.getDrawable(icon), lightColor, darkColor);
+    }
+
     @Override
     public void setLayoutDirection(int layoutDirection) {
         // Reload all the icons
@@ -439,6 +459,8 @@
         setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
         getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
 
+        getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+
         setDisabledFlags(mDisabledFlags, true);
 
         mBarTransitions.reapplyDarkIntensity();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 21a96e2..cce9d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -39,7 +39,7 @@
         }
     }
 
-    private KeyButtonDrawable(Drawable[] drawables) {
+    protected KeyButtonDrawable(Drawable[] drawables) {
         super(drawables);
         for (int i = 0; i < drawables.length; i++) {
             setLayerGravity(i, Gravity.CENTER);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java
new file mode 100644
index 0000000..acf9c00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.systemui.statusbar.policy;
+
+import android.annotation.ColorInt;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.graphics.ColorUtils;
+
+/**
+ * Drawable for {@link KeyButtonView}s which contains a single asset and colors for light and dark
+ * navigation bar mode.
+ */
+public class TintedKeyButtonDrawable extends KeyButtonDrawable {
+
+    private final int mLightColor;
+    private final int mDarkColor;
+
+    public static TintedKeyButtonDrawable create(Drawable drawable, @ColorInt int lightColor,
+            @ColorInt int darkColor) {
+        return new TintedKeyButtonDrawable(new Drawable[] { drawable }, lightColor, darkColor);
+    }
+
+    private TintedKeyButtonDrawable(Drawable[] drawables, int lightColor, int darkColor){
+        super(drawables);
+        mLightColor = lightColor;
+        mDarkColor = darkColor;
+    }
+
+    @Override
+    public void setDarkIntensity(float intensity) {
+        // Duplicate intensity scaling from KeyButtonDrawable
+        int intermediateColor = ColorUtils.compositeColors(
+                setAlphaFloat(mDarkColor, intensity),
+                setAlphaFloat(mLightColor,1f - intensity));
+        getDrawable(0).setTint(intermediateColor);
+        invalidateSelf();
+    }
+
+    private int setAlphaFloat(int color, float alpha) {
+        return ColorUtils.setAlphaComponent(color, (int) (alpha * 255f));
+    }
+}