Merge "Translate and Scale the status area with the large weather clock" into udc-dev

This commit is contained in:
Hawkwood Glazier
2023-06-09 16:58:19 +00:00
committed by Android (Google) Code Review
12 changed files with 310 additions and 24 deletions

View File

@@ -43,7 +43,7 @@
<!-- Not quite optimal but needed to translate these items as a group. The
NotificationIconContainer has its own logic for translation. -->
<LinearLayout
<com.android.keyguard.KeyguardStatusAreaView
android:id="@+id/keyguard_status_area"
android:orientation="vertical"
android:layout_width="match_parent"
@@ -63,5 +63,5 @@
android:paddingStart="@dimen/below_clock_padding_start_icons"
android:visibility="invisible"
/>
</LinearLayout>
</com.android.keyguard.KeyguardStatusAreaView>
</com.android.keyguard.KeyguardClockSwitch>

View File

@@ -16,5 +16,5 @@
-->
<resources>
<!-- Invisibility to use for the date & weather view when it is disabled by a clock -->
<integer name="keyguard_date_weather_view_invisibility">8</integer>
<integer name="keyguard_date_weather_view_invisibility">4</integer>
</resources>

View File

@@ -148,4 +148,9 @@
<dimen name="default_dot_diameter">34dp</dimen>
<dimen name="default_dot_spacing">0dp</dimen>
<!-- Weather clock smartspace scaling to apply for the weather clock -->
<item name="weather_clock_smartspace_scale" type="dimen" format="float">1.0</item>
<dimen name="weather_clock_smartspace_translateX">0dp</dimen>
<dimen name="weather_clock_smartspace_translateY">0dp</dimen>
</resources>

View File

@@ -17,4 +17,18 @@
<resources>
<item type="id" name="header_footer_views_added_tag_key" />
<!-- animation channels for keyguard status area -->
<item type="id" name="translate_x_clock_design_animator_tag" />
<item type="id" name="translate_x_clock_design_animator_start_tag" />
<item type="id" name="translate_x_clock_design_animator_end_tag" />
<item type="id" name="translate_x_aod_animator_tag" />
<item type="id" name="translate_x_aod_animator_start_tag" />
<item type="id" name="translate_x_aod_animator_end_tag" />
<item type="id" name="translate_y_clock_size_animator_tag" />
<item type="id" name="translate_y_clock_size_animator_start_tag" />
<item type="id" name="translate_y_clock_size_animator_end_tag" />
<item type="id" name="translate_y_clock_design_animator_tag" />
<item type="id" name="translate_y_clock_design_animator_start_tag" />
<item type="id" name="translate_y_clock_design_animator_end_tag" />
</resources>

View File

@@ -65,8 +65,8 @@ class UnfoldConstantTranslateAnimator(
} else {
1
}
viewsToTranslate.forEach { (view, direction) ->
view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier
viewsToTranslate.forEach { (view, direction, func) ->
view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) }
}
}
@@ -77,7 +77,7 @@ class UnfoldConstantTranslateAnimator(
.filter { it.shouldBeAnimated() }
.mapNotNull {
parent.findViewById<View>(it.viewId)?.let { view ->
ViewToTranslate(WeakReference(view), it.direction)
ViewToTranslate(WeakReference(view), it.direction, it.translateFunc)
}
}
.toList()
@@ -91,14 +91,19 @@ class UnfoldConstantTranslateAnimator(
data class ViewIdToTranslate(
val viewId: Int,
val direction: Direction,
val shouldBeAnimated: () -> Boolean = { true }
val shouldBeAnimated: () -> Boolean = { true },
val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value },
)
/**
* Represents a view whose animation process is in-progress. It should be immutable because the
* started animation should be completed.
*/
private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction)
private data class ViewToTranslate(
val view: WeakReference<View>,
val direction: Direction,
val translateFunc: (View, Float) -> Unit,
)
/** Direction of the animation. */
enum class Direction(val multiplier: Float) {

View File

@@ -1,8 +1,14 @@
package com.android.keyguard;
import static android.view.View.ALPHA;
import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_Y;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -17,6 +23,7 @@ import android.widget.RelativeLayout;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.res.ResourcesCompat;
import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
@@ -44,6 +51,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
private static final long STATUS_AREA_START_DELAY_MILLIS = 0;
private static final long STATUS_AREA_MOVE_UP_MILLIS = 967;
private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467;
private static final float SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER = 1.4f;
@IntDef({LARGE, SMALL})
@Retention(RetentionPolicy.SOURCE)
@@ -88,14 +96,18 @@ public class KeyguardClockSwitch extends RelativeLayout {
private KeyguardClockFrame mLargeClockFrame;
private ClockController mClock;
private View mStatusArea;
private KeyguardStatusAreaView mStatusArea;
private int mSmartspaceTopOffset;
private float mWeatherClockSmartspaceScaling = 1f;
private int mWeatherClockSmartspaceTranslateX = 0;
private int mWeatherClockSmartspaceTranslateY = 0;
private int mDrawAlpha = 255;
/**
* Maintain state so that a newly connected plugin can be initialized.
*/
private float mDarkAmount;
private boolean mSplitShadeCentered = false;
/**
* Indicates which clock is currently displayed - should be one of {@link ClockSize}.
@@ -105,7 +117,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
@VisibleForTesting AnimatorSet mClockInAnim = null;
@VisibleForTesting AnimatorSet mClockOutAnim = null;
private AnimatorSet mStatusAreaAnim = null;
@VisibleForTesting AnimatorSet mStatusAreaAnim = null;
private int mClockSwitchYAmount;
@VisibleForTesting boolean mChildrenAreLaidOut = false;
@@ -117,13 +129,30 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
/**
* Apply dp changes on font/scale change
* Apply dp changes on configuration change
*/
public void onDensityOrFontScaleChanged() {
public void onConfigChanged() {
mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_clock_switch_y_shift);
mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_smartspace_top_offset);
mWeatherClockSmartspaceScaling = ResourcesCompat.getFloat(
mContext.getResources(), R.dimen.weather_clock_smartspace_scale);
mWeatherClockSmartspaceTranslateX = mContext.getResources().getDimensionPixelSize(
R.dimen.weather_clock_smartspace_translateX);
mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize(
R.dimen.weather_clock_smartspace_translateY);
updateStatusArea(/* animate= */false);
}
/**
* Enable or disable split shade specific positioning
*/
public void setSplitShadeCentered(boolean splitShadeCentered) {
if (mSplitShadeCentered != splitShadeCentered) {
mSplitShadeCentered = splitShadeCentered;
updateStatusArea(/* animate= */true);
}
}
@Override
@@ -134,7 +163,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
mStatusArea = findViewById(R.id.keyguard_status_area);
onDensityOrFontScaleChanged();
onConfigChanged();
}
@Override
@@ -182,6 +211,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
mSmallClockFrame.addView(clock.getSmallClock().getView());
mLargeClockFrame.addView(clock.getLargeClock().getView());
updateClockTargetRegions();
updateStatusArea(/* animate= */false);
}
private void updateStatusArea(boolean animate) {
if (mDisplayedClockSize != null && mChildrenAreLaidOut) {
updateClockViews(mDisplayedClockSize == LARGE, animate);
}
}
void updateClockTargetRegions() {
@@ -230,13 +266,25 @@ public class KeyguardClockSwitch extends RelativeLayout {
mStatusAreaAnim = null;
View in, out;
float statusAreaYTranslation, clockInYTranslation, clockOutYTranslation;
float statusAreaYTranslation, statusAreaClockScale = 1f;
float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f;
float clockInYTranslation, clockOutYTranslation;
if (useLargeClock) {
out = mSmallClockFrame;
in = mLargeClockFrame;
if (indexOfChild(in) == -1) addView(in, 0);
statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop()
+ mSmartspaceTopOffset;
// TODO: Load from clock config when less risky
if (mClock != null
&& mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) {
statusAreaClockScale = mWeatherClockSmartspaceScaling;
statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX;
statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY;
if (mSplitShadeCentered) {
statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER;
}
}
clockInYTranslation = 0;
clockOutYTranslation = 0; // Small clock translation is handled with statusArea
} else {
@@ -258,7 +306,12 @@ public class KeyguardClockSwitch extends RelativeLayout {
in.setAlpha(1f);
in.setTranslationY(clockInYTranslation);
in.setVisibility(View.VISIBLE);
mStatusArea.setTranslationY(statusAreaYTranslation);
mStatusArea.setScaleX(statusAreaClockScale);
mStatusArea.setScaleY(statusAreaClockScale);
mStatusArea.setTranslateXFromClockDesign(statusAreaClockTranslateX);
mStatusArea.setTranslateYFromClockDesign(statusAreaClockTranslateY);
mStatusArea.setTranslateYFromClockSize(statusAreaYTranslation);
mSmallClockFrame.setTranslationY(statusAreaYTranslation);
return;
}
@@ -295,8 +348,15 @@ public class KeyguardClockSwitch extends RelativeLayout {
useLargeClock ? STATUS_AREA_MOVE_UP_MILLIS : STATUS_AREA_MOVE_DOWN_MILLIS);
mStatusAreaAnim.setInterpolator(Interpolators.EMPHASIZED);
mStatusAreaAnim.playTogether(
ObjectAnimator.ofFloat(mStatusArea, TRANSLATION_Y, statusAreaYTranslation),
ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation));
ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_SIZE.getProperty(),
statusAreaYTranslation),
ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation),
ObjectAnimator.ofFloat(mStatusArea, SCALE_X, statusAreaClockScale),
ObjectAnimator.ofFloat(mStatusArea, SCALE_Y, statusAreaClockScale),
ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_X_CLOCK_DESIGN.getProperty(),
statusAreaClockTranslateX),
ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_DESIGN.getProperty(),
statusAreaClockTranslateY));
mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
mStatusAreaAnim = null;

View File

@@ -330,10 +330,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
/**
* Apply dp changes on font/scale change
* Apply dp changes on configuration change
*/
public void onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged();
public void onConfigChanged() {
mView.onConfigChanged();
mKeyguardSmallClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
mKeyguardLargeClockTopMargin =
@@ -344,6 +344,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
setDateWeatherVisibility();
}
/**
* Enable or disable split shade center specific positioning
*/
public void setSplitShadeCentered(boolean splitShadeCentered) {
mView.setSplitShadeCentered(splitShadeCentered);
}
/**
* Set which clock should be displayed on the keyguard. The other one will be automatically
@@ -407,7 +413,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
scale, props, animate);
if (mStatusArea != null) {
PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
PropertyAnimator.setProperty(mStatusArea, KeyguardStatusAreaView.TRANSLATE_X_AOD,
x, props, animate);
}
}

View File

@@ -0,0 +1,118 @@
package com.android.keyguard
import android.content.Context
import android.util.AttributeSet
import android.util.FloatProperty
import android.widget.LinearLayout
import com.android.systemui.R
import com.android.systemui.statusbar.notification.AnimatableProperty
class KeyguardStatusAreaView(
context: Context,
attrs: AttributeSet? = null,
) : LinearLayout(context, attrs) {
var translateXFromClockDesign = 0f
get() = field
set(value) {
field = value
translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold
}
var translateXFromAod = 0f
get() = field
set(value) {
field = value
translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold
}
var translateXFromUnfold = 0F
get() = field
set(value) {
field = value
translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold
}
var translateYFromClockSize = 0f
get() = field
set(value) {
field = value
translationY = value + translateYFromClockDesign
}
var translateYFromClockDesign = 0f
get() = field
set(value) {
field = value
translationY = value + translateYFromClockSize
}
companion object {
@JvmField
val TRANSLATE_X_CLOCK_DESIGN =
AnimatableProperty.from(
object : FloatProperty<KeyguardStatusAreaView>("TranslateXClockDesign") {
override fun setValue(view: KeyguardStatusAreaView, value: Float) {
view.translateXFromClockDesign = value
}
override fun get(view: KeyguardStatusAreaView): Float {
return view.translateXFromClockDesign
}
},
R.id.translate_x_clock_design_animator_tag,
R.id.translate_x_clock_design_animator_start_tag,
R.id.translate_x_clock_design_animator_end_tag
)
@JvmField
val TRANSLATE_X_AOD =
AnimatableProperty.from(
object : FloatProperty<KeyguardStatusAreaView>("TranslateXAod") {
override fun setValue(view: KeyguardStatusAreaView, value: Float) {
view.translateXFromAod = value
}
override fun get(view: KeyguardStatusAreaView): Float {
return view.translateXFromAod
}
},
R.id.translate_x_aod_animator_tag,
R.id.translate_x_aod_animator_start_tag,
R.id.translate_x_aod_animator_end_tag
)
@JvmField
val TRANSLATE_Y_CLOCK_SIZE =
AnimatableProperty.from(
object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockSize") {
override fun setValue(view: KeyguardStatusAreaView, value: Float) {
view.translateYFromClockSize = value
}
override fun get(view: KeyguardStatusAreaView): Float {
return view.translateYFromClockSize
}
},
R.id.translate_y_clock_size_animator_tag,
R.id.translate_y_clock_size_animator_start_tag,
R.id.translate_y_clock_size_animator_end_tag
)
@JvmField
val TRANSLATE_Y_CLOCK_DESIGN =
AnimatableProperty.from(
object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockDesign") {
override fun setValue(view: KeyguardStatusAreaView, value: Float) {
view.translateYFromClockDesign = value
}
override fun get(view: KeyguardStatusAreaView): Float {
return view.translateYFromClockDesign
}
},
R.id.translate_y_clock_design_animator_tag,
R.id.translate_y_clock_design_animator_start_tag,
R.id.translate_y_clock_design_animator_end_tag
)
}
}

View File

@@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.transition.ChangeBounds;
import android.transition.Transition;
@@ -280,8 +281,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
@Override
public void onDensityOrFontScaleChanged() {
mKeyguardClockSwitchController.onDensityOrFontScaleChanged();
public void onConfigChanged(Configuration newConfig) {
mKeyguardClockSwitchController.onConfigChanged();
}
};
@@ -329,6 +330,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
boolean splitShadeEnabled,
boolean shouldBeCentered,
boolean animate) {
mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered);
if (mStatusViewCentered == shouldBeCentered) {
return;
}

View File

@@ -53,7 +53,10 @@ constructor(
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
setOf(
ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard),
ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard,
{ view, value ->
(view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
}),
ViewIdToTranslate(
R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly),
ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard),

View File

@@ -72,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
private KeyguardStatusAreaView mStatusArea;
KeyguardClockSwitch mKeyguardClockSwitch;
@@ -109,6 +110,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area);
mKeyguardClockSwitch.mChildrenAreLaidOut = true;
}
@@ -185,6 +187,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
mKeyguardClockSwitch.mStatusAreaAnim.end();
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
@@ -206,6 +209,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
mKeyguardClockSwitch.mStatusAreaAnim.end();
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
@@ -225,6 +229,31 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
}
@Test
public void switchingToSmallClockAnimation_resetsStatusArea() {
mKeyguardClockSwitch.switchToClock(SMALL, true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
mKeyguardClockSwitch.mStatusAreaAnim.end();
assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
assertThat(mStatusArea.getScaleX()).isEqualTo(1);
assertThat(mStatusArea.getScaleY()).isEqualTo(1);
}
@Test
public void switchingToSmallClockNoAnimation_resetsStatusArea() {
mKeyguardClockSwitch.switchToClock(SMALL, false);
assertThat(mStatusArea.getTranslationX()).isEqualTo(0);
assertThat(mStatusArea.getTranslationY()).isEqualTo(0);
assertThat(mStatusArea.getScaleX()).isEqualTo(1);
assertThat(mStatusArea.getScaleY()).isEqualTo(1);
}
@Test
public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();

View File

@@ -0,0 +1,44 @@
package com.android.keyguard
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class KeyguardStatusAreaViewTest : SysuiTestCase() {
private lateinit var view: KeyguardStatusAreaView
@Before
fun setUp() {
view = KeyguardStatusAreaView(context)
}
@Test
fun checkTranslationX_AddedTotals() {
view.translateXFromClockDesign = 10f
assertEquals(10f, view.translationX)
view.translateXFromAod = 20f
assertEquals(30f, view.translationX)
view.translateXFromUnfold = 30f
assertEquals(60f, view.translationX)
}
@Test
fun checkTranslationY_AddedTotals() {
view.translateYFromClockSize = 10f
assertEquals(10f, view.translationY)
view.translateYFromClockDesign = 20f
assertEquals(30f, view.translationY)
}
}