Translate and Scale the status area with the large weather clock

This is intended to better position smartspace when using the weather
clock on certain devices, but does this by adjusting the entire
status area. In practice this is fine as smartspace is the only child
view that's rendered in this case.

Bug: 283308908
Test: Manually checked on several device
Change-Id: I75378c6a2732f8486eef49b91a1c24caed002f1a
Merged-In: I75378c6a2732f8486eef49b91a1c24caed002f1a
This commit is contained in:
Hawkwood Glazier
2023-05-24 14:59:30 +00:00
parent 765c65d945
commit 0b558b4fe0
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)
}
}