Loading packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml 0 → 100644 +117 −0 Original line number Diff line number Diff line <?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 packages/SystemUI/res/layout/rotate_suggestion.xml 0 → 100644 +32 −0 Original line number Diff line number Diff line <?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> packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +4 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +155 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +4 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); Loading @@ -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); Loading Loading
packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml 0 → 100644 +117 −0 Original line number Diff line number Diff line <?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
packages/SystemUI/res/layout/rotate_suggestion.xml 0 → 100644 +32 −0 Original line number Diff line number Diff line <?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>
packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +4 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +155 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +4 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); Loading @@ -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); Loading