Reduce usages of ContextThemeWrapper causing memory churn

- Cache light/dark icon colors so we don't have to create additional
  theme wrappers when updating them
- Convert rotation styles to separate drawables so we can skip creating
  theme wrappers just to change some attributes of the drawable, these
  icons are not changed much so it's worth the memory savings even if
  this introduces copies of the drawable
- Clean up floating rotation drawable initialization slightly, only create
  when updating the icon, and only start the animation when showing the
  drawable
- Removed some unused code

Bug: 157510126
Test: atest SystemUiTests
Test: Verify rotation icon looks the same in all orientations with both
      three button and gestural, and also with light/dark nav bar

Signed-off-by: Winson Chung <winsonc@google.com>
Change-Id: I31e89f7aef00b38f2a7953d6e229c9ed7df8b479
This commit is contained in:
Winson Chung
2020-07-18 02:58:58 +00:00
parent 469a3084b7
commit 99fbbf8717
16 changed files with 663 additions and 166 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2017 The Android Open Source Project
Copyright (C) 2020 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.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
android:scaleX="?attr/rotateButtonScaleX">
android:scaleX="1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonEndAngle">
android:valueFrom="0"
android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonStartAngle"/>
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonEndAngle">
android:valueFrom="0"
android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonStartAngle"/>
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonEndAngle">
android:valueFrom="0"
android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonStartAngle"/>
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonEndAngle">
android:valueFrom="0"
android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonStartAngle"/>
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="?attr/rotateButtonStartAngle"
android:valueTo="?attr/rotateButtonEndAngle">
android:valueFrom="0"
android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 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:name="root"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28.0"
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
android:scaleX="1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
</group>
</vector>
</aapt:attr>
<!-- Repeat all animations 5 times but don't fade out at the end -->
<target android:name="root">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
</set>
</aapt:attr>
</target>
<target android:name="icon">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
android:valueFrom="90"
android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
</animated-vector>

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 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:name="root"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28.0"
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
android:scaleX="-1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
</group>
</vector>
</aapt:attr>
<!-- Repeat all animations 5 times but don't fade out at the end -->
<target android:name="root">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
</set>
</aapt:attr>
</target>
<target android:name="icon">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
android:valueFrom="0"
android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="0"
android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="0"
android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="0"
android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="0"
android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="0"
android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
</animated-vector>

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 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:name="root"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28.0"
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
android:scaleX="-1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
</group>
</vector>
</aapt:attr>
<!-- Repeat all animations 5 times but don't fade out at the end -->
<target android:name="root">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
<!-- Linear fade out -->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="1700"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:anim/linear_interpolator"/>
<!-- Linear fade in-->
<objectAnimator android:propertyName="alpha"
android:duration="100"
android:startOffset="100"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:anim/linear_interpolator" />
</set>
</aapt:attr>
</target>
<target android:name="icon">
<aapt:attr name="android:animation">
<set android:ordering="sequentially">
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
android:valueFrom="90"
android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
<!-- Reset rotation position for fade in -->
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
android:valueFrom="90"
android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
android:valueFrom="90"
android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
</animated-vector>

View File

@@ -133,11 +133,6 @@
<attr name="buttonStrokeWidth" format="dimension" />
</declare-styleable>
<!-- Used to style rotate suggestion button AVD animations -->
<attr name="rotateButtonStartAngle" format="float" />
<attr name="rotateButtonEndAngle" format="float" />
<attr name="rotateButtonScaleX" format="float" />
<!-- Used to style charging animation AVD animation -->
<attr name="chargingAnimColor" format="color" />

View File

@@ -604,31 +604,6 @@
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
<!-- Used to style rotate suggestion button AVD animations -->
<style name="RotateButtonCCWStart0">
<item name="rotateButtonStartAngle">0</item>
<item name="rotateButtonEndAngle">-90</item>
<item name="rotateButtonScaleX">1</item>
</style>
<style name="RotateButtonCCWStart90">
<item name="rotateButtonStartAngle">90</item>
<item name="rotateButtonEndAngle">0</item>
<item name="rotateButtonScaleX">1</item>
</style>
<style name="RotateButtonCWStart0">
<item name="rotateButtonStartAngle">0</item>
<item name="rotateButtonEndAngle">90</item>
<item name="rotateButtonScaleX">-1</item>
</style>
<style name="RotateButtonCWStart90">
<item name="rotateButtonStartAngle">90</item>
<item name="rotateButtonEndAngle">180</item>
<item name="rotateButtonScaleX">-1</item>
</style>
<style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">@android:color/white</item>

View File

@@ -50,12 +50,12 @@ public class ContextualButton extends ButtonDispatcher {
/**
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
public void updateIcon() {
public void updateIcon(int lightIconColor, int darkIconColor) {
if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
KeyButtonDrawable drawable = getNewDrawable();
KeyButtonDrawable drawable = getNewDrawable(lightIconColor, darkIconColor);
if (currentDrawable != null) {
drawable.setDarkIntensity(currentDrawable.getDarkIntensity());
}
@@ -116,9 +116,9 @@ public class ContextualButton extends ButtonDispatcher {
mGroup = group;
}
protected KeyButtonDrawable getNewDrawable() {
return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId,
false /* shadow */);
protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
return KeyButtonDrawable.create(getContext().getApplicationContext(), lightIconColor,
darkIconColor, mIconResId, false /* shadow */, null /* ovalBackground */);
}
/**

View File

@@ -111,9 +111,9 @@ public class ContextualButtonGroup extends ButtonDispatcher {
* Update all the icons that are attached to this group. This will get all the buttons to update
* their icons for their buttons.
*/
public void updateIcons() {
public void updateIcons(int lightIconColor, int darkIconColor) {
for (ButtonData data : mButtonData) {
data.button.updateIcon();
data.button.updateIcon(lightIconColor, darkIconColor);
}
}

View File

@@ -16,19 +16,16 @@
package com.android.systemui.statusbar.phone;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.KeyButtonView;
@@ -65,6 +62,8 @@ public class FloatingRotationButton implements RotationButton {
@Override
public void setRotationButtonController(RotationButtonController rotationButtonController) {
mRotationButtonController = rotationButtonController;
updateIcon(mRotationButtonController.getLightIconColor(),
mRotationButtonController.getDarkIconColor());
}
@Override
@@ -101,7 +100,6 @@ public class FloatingRotationButton implements RotationButton {
default:
break;
}
updateIcon();
mWindowManager.addView(mKeyButtonView, lp);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
@@ -126,17 +124,13 @@ public class FloatingRotationButton implements RotationButton {
}
@Override
public void updateIcon() {
if (!mIsShowing) {
return;
}
mKeyButtonDrawable = getImageDrawable();
public void updateIcon(int lightIconColor, int darkIconColor) {
Color ovalBackgroundColor = Color.valueOf(Color.red(darkIconColor),
Color.green(darkIconColor), Color.blue(darkIconColor), BACKGROUND_ALPHA);
mKeyButtonDrawable = KeyButtonDrawable.create(mRotationButtonController.getContext(),
lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
false /* shadow */, ovalBackgroundColor);
mKeyButtonView.setImageDrawable(mKeyButtonDrawable);
mKeyButtonDrawable.setCallback(mKeyButtonView);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
mKeyButtonDrawable.startAnimation();
}
}
@Override
@@ -151,20 +145,7 @@ public class FloatingRotationButton implements RotationButton {
@Override
public KeyButtonDrawable getImageDrawable() {
Context context = new ContextThemeWrapper(mContext.getApplicationContext(),
mRotationButtonController.getStyleRes());
final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme);
Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme);
@ColorInt int darkColor = Utils.getColorAttrDefaultColor(darkContext,
R.attr.singleToneColor);
Color ovalBackgroundColor = Color.valueOf(Color.red(darkColor), Color.green(darkColor),
Color.blue(darkColor), BACKGROUND_ALPHA);
return KeyButtonDrawable.create(lightContext,
Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor), darkColor,
R.drawable.ic_sysbar_rotate_button, false /* shadow */, ovalBackgroundColor);
return mKeyButtonDrawable;
}
@Override

View File

@@ -48,6 +48,7 @@ import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -63,6 +64,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -124,6 +126,9 @@ public class NavigationBarView extends FrameLayout implements
private KeyButtonDrawable mHomeDefaultIcon;
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
private Context mLightContext;
private int mLightIconColor;
private int mDarkIconColor;
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
@@ -278,6 +283,12 @@ public class NavigationBarView extends FrameLayout implements
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
final Context darkContext = new ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.darkIconTheme));
mLightContext = new ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.lightIconTheme));
mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor);
mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
mIsVertical = false;
mLongClickableAccessibilityButton = false;
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
@@ -290,7 +301,7 @@ public class NavigationBarView extends FrameLayout implements
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
R.drawable.ic_ime_switcher_default);
final RotationContextButton rotateSuggestionButton = new RotationContextButton(
R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button);
R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
final ContextualButton accessibilityButton =
new ContextualButton(R.id.accessibility_button,
R.drawable.ic_sysbar_accessibility_button);
@@ -303,8 +314,8 @@ public class NavigationBarView extends FrameLayout implements
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mFloatingRotationButton = new FloatingRotationButton(context);
mRotationButtonController = new RotationButtonController(context,
R.style.RotateButtonCCWStart90,
mRotationButtonController = new RotationButtonController(mLightContext,
mLightIconColor, mDarkIconColor,
isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);
mConfiguration = new Configuration();
@@ -501,7 +512,7 @@ public class NavigationBarView extends FrameLayout implements
}
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
mContextualButtonGroup.updateIcons();
mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor);
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -559,11 +570,6 @@ public class NavigationBarView extends FrameLayout implements
drawable.setRotation(mIsVertical ? 90 : 0);
}
private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
@DrawableRes int quickStepIcon) {
return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon));
}
private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
@DrawableRes int quickStepIcon) {
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
@@ -571,11 +577,8 @@ public class NavigationBarView extends FrameLayout implements
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
return KeyButtonDrawable.create(mContext, icon, hasShadow);
return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
true /* hasShadow */, null /* ovalBackgroundColor */);
}
/** To be called when screen lock/unlock state changes */
@@ -861,7 +864,6 @@ public class NavigationBarView extends FrameLayout implements
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
if (isGesturalMode(mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);

View File

@@ -27,7 +27,7 @@ interface RotationButton {
boolean show();
boolean hide();
boolean isVisible();
void updateIcon();
void updateIcon(int lightIconColor, int darkIconColor);
void setOnClickListener(View.OnClickListener onClickListener);
void setOnHoverListener(View.OnHoverListener onHoverListener);
KeyButtonDrawable getImageDrawable();

View File

@@ -21,6 +21,8 @@ import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.StyleRes;
import android.app.StatusBarManager;
import android.content.ContentResolver;
@@ -30,6 +32,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.IRotationWatcher.Stub;
import android.view.MotionEvent;
import android.view.Surface;
@@ -40,6 +43,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -61,10 +65,12 @@ public class RotationButtonController {
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
private final Context mContext;
private final RotationButton mRotationButton;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private final ViewRippler mViewRippler = new ViewRippler();
private @StyleRes int mStyleRes;
private int mLastRotationSuggestion;
private boolean mPendingRotationSuggestion;
private boolean mHoveringRotationSuggestion;
@@ -75,6 +81,9 @@ public class RotationButtonController {
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
private boolean mSkipOverrideUserLockPrefsOnce;
private int mLightIconColor;
private int mDarkIconColor;
private int mIconResId = R.drawable.ic_sysbar_rotate_button_ccw_start_90;
private final Runnable mRemoveRotationProposal =
() -> setRotateSuggestionButtonState(false /* visible */);
@@ -82,9 +91,6 @@ public class RotationButtonController {
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
private final Context mContext;
private final RotationButton mRotationButton;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final Stub mRotationWatcher = new Stub() {
@Override
@@ -117,12 +123,14 @@ public class RotationButtonController {
return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
}
RotationButtonController(Context context, @StyleRes int style, RotationButton rotationButton) {
RotationButtonController(Context context, @ColorInt int lightIconColor,
@ColorInt int darkIconColor, RotationButton rotationButton) {
mContext = context;
mLightIconColor = lightIconColor;
mDarkIconColor = darkIconColor;
mRotationButton = rotationButton;
mRotationButton.setRotationButtonController(this);
mStyleRes = style;
mIsNavigationBarShowing = true;
mRotationLockController = Dependency.get(RotationLockController.class);
mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
@@ -275,17 +283,20 @@ public class RotationButtonController {
return;
}
// TODO: Remove styles?
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
int style;
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
style = rotationCCW ? R.style.RotateButtonCCWStart90 : R.style.RotateButtonCWStart90;
mIconResId = rotationCCW
? R.drawable.ic_sysbar_rotate_button_ccw_start_90
: R.drawable.ic_sysbar_rotate_button_cw_start_90;
} else { // 90 or 270
style = rotationCCW ? R.style.RotateButtonCCWStart0 : R.style.RotateButtonCWStart0;
mIconResId = rotationCCW
? R.drawable.ic_sysbar_rotate_button_ccw_start_0
: R.drawable.ic_sysbar_rotate_button_ccw_start_0;
}
mStyleRes = style;
mRotationButton.updateIcon();
mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
if (mIsNavigationBarShowing) {
// The navbar is visible so show the icon right away
@@ -316,14 +327,26 @@ public class RotationButtonController {
}
}
@StyleRes int getStyleRes() {
return mStyleRes;
Context getContext() {
return mContext;
}
RotationButton getRotationButton() {
return mRotationButton;
}
@DrawableRes int getIconResId() {
return mIconResId;
}
@ColorInt int getLightIconColor() {
return mLightIconColor;
}
@ColorInt int getDarkIconColor() {
return mDarkIconColor;
}
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();

View File

@@ -16,22 +16,16 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.content.Context;
import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
/** Containing logic for the rotation button in nav bar. */
public class RotationContextButton extends ContextualButton implements
NavigationModeController.ModeChangedListener, RotationButton {
public class RotationContextButton extends ContextualButton implements RotationButton {
public static final boolean DEBUG_ROTATION = false;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private RotationButtonController mRotationButtonController;
public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
@@ -56,16 +50,10 @@ public class RotationContextButton extends ContextualButton implements
}
@Override
protected KeyButtonDrawable getNewDrawable() {
Context context = new ContextThemeWrapper(getContext().getApplicationContext(),
mRotationButtonController.getStyleRes());
return KeyButtonDrawable.create(context, mIconResId, false /* shadow */,
null /* ovalBackgroundColor */);
}
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
return KeyButtonDrawable.create(mRotationButtonController.getContext(),
lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
false /* shadow */, null /* ovalBackgroundColor */);
}
@Override

View File

@@ -37,7 +37,6 @@ import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.settingslib.Utils;
@@ -438,34 +437,6 @@ public class KeyButtonDrawable extends Drawable {
}
}
/**
* Creates a KeyButtonDrawable with a shadow given its icon. The tint applied to the drawable
* is determined by the dark and light theme given by the context.
* @param ctx Context to get the drawable and determine the dark and light theme
* @param icon the icon resource id
* @param hasShadow if a shadow will appear with the drawable
* @param ovalBackgroundColor the color of the oval bg that will be drawn
* @return KeyButtonDrawable
*/
public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
boolean hasShadow, Color ovalBackgroundColor) {
final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow,
ovalBackgroundColor);
}
/**
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
* {@link #create(Context, int, boolean, boolean)}.
*/
public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
boolean hasShadow) {
return create(ctx, icon, hasShadow, null /* ovalBackgroundColor */);
}
/**
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
* {@link #create(Context, int, boolean, boolean)}.

View File

@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -190,13 +191,13 @@ public class NavigationBarContextTest extends SysuiTestCase {
kbd2.setDarkIntensity(0f);
// Update icon returns the drawable intensity to half
doReturn(kbd1).when(button).getNewDrawable();
button.updateIcon();
doReturn(kbd1).when(button).getNewDrawable(anyInt(), anyInt());
button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd1.getDarkIntensity(), DARK_INTENSITY_ERR);
// Return old dark intensity on new drawable after update icon
doReturn(kbd2).when(button).getNewDrawable();
button.updateIcon();
doReturn(kbd2).when(button).getNewDrawable(anyInt(), anyInt());
button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd2.getDarkIntensity(), DARK_INTENSITY_ERR);
}

View File

@@ -59,8 +59,8 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
mRotationButtonController = spy(
new RotationButtonController(mContext, RES_UNDEF, mRotationButton));
mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0,
mRotationButton));
final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();