Prevent alpha transitions from clipping out-of-bounds clocks

In order to do this, we bypass the default alpha mechaism used by views,
and call saveLayerAlpha ourselves on several views. This allows us to
set bounds for this area outside of the measured bounds.

Clipping still appears to occur after a user switch. b/282226587 will
track that follow up issue.

This is effectively a revert of Ia73ea00920ca0147e0acc5d2919b457203b7575e

Bug: 280113068
Bug: 281697986
Test: Manually checked several transitions
Change-Id: I9112efab88facd0f32daa5cd7377b8184658c159
This commit is contained in:
Hawkwood Glazier
2023-05-11 23:02:45 +00:00
parent 7e39238a1f
commit 93e23c58dc
10 changed files with 107 additions and 80 deletions

View File

@@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:layout_gravity="center_horizontal|top">
<FrameLayout
<com.android.keyguard.KeyguardClockFrame
android:id="@+id/lockscreen_clock_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/small_clock_height"
@@ -34,7 +34,7 @@
android:clipChildren="false"
android:paddingStart="@dimen/clock_padding_start"
android:visibility="invisible" />
<FrameLayout
<com.android.keyguard.KeyguardClockFrame
android:id="@+id/lockscreen_clock_view_large"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@@ -30,7 +30,7 @@
android:clipChildren="false"
android:layout_width="0dp"
android:layout_height="wrap_content">
<LinearLayout
<com.android.keyguard.KeyguardStatusContainer
android:id="@+id/status_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -48,5 +48,5 @@
android:layout_height="wrap_content"
android:padding="@dimen/qs_media_padding"
/>
</LinearLayout>
</com.android.keyguard.KeyguardStatusContainer>
</com.android.keyguard.KeyguardStatusView>

View File

@@ -0,0 +1,39 @@
package com.android.keyguard
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
class KeyguardClockFrame(
context: Context,
attrs: AttributeSet,
) : FrameLayout(context, attrs) {
private var drawAlpha: Int = 255
protected override fun onSetAlpha(alpha: Int): Boolean {
drawAlpha = alpha
return true
}
protected override fun dispatchDraw(canvas: Canvas) {
val restoreTo = saveCanvasAlpha(this, canvas, drawAlpha)
super.dispatchDraw(canvas)
canvas.restoreToCount(restoreTo)
}
companion object {
@JvmStatic
fun saveCanvasAlpha(view: View, canvas: Canvas, alpha: Int): Int {
var (x, y) =
run {
val locationOnScreen = IntArray(2)
view.getLocationOnScreen(locationOnScreen)
Pair(locationOnScreen[0].toFloat(), locationOnScreen[1].toFloat())
}
return canvas.saveLayerAlpha(-1f * x, -1f * y, x + view.width, y + view.height, alpha)
}
}
}

View File

@@ -5,11 +5,11 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.annotation.IntDef;
@@ -69,12 +69,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
/**
* Frame for small/large clocks
*/
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
private KeyguardClockFrame mSmallClockFrame;
private KeyguardClockFrame mLargeClockFrame;
private ClockController mClock;
private View mStatusArea;
private int mSmartspaceTopOffset;
private int mDrawAlpha = 255;
/**
* Maintain state so that a newly connected plugin can be initialized.
@@ -121,6 +122,19 @@ public class KeyguardClockSwitch extends RelativeLayout {
onDensityOrFontScaleChanged();
}
@Override
protected boolean onSetAlpha(int alpha) {
mDrawAlpha = alpha;
return true;
}
@Override
protected void dispatchDraw(Canvas canvas) {
int restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, mDrawAlpha);
super.dispatchDraw(canvas);
canvas.restoreToCount(restoreTo);
}
public void setLogBuffer(LogBuffer logBuffer) {
mLogBuffer = logBuffer;
}

View File

@@ -178,20 +178,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
}
/**
* Set alpha directly to mView will clip clock, so we set alpha to clock face instead
*/
public void setAlpha(float alpha) {
ClockController clock = getClock();
if (clock != null) {
clock.getLargeClock().getView().setAlpha(alpha);
clock.getSmallClock().getView().setAlpha(alpha);
}
if (mStatusArea != null) {
mStatusArea.setAlpha(alpha);
}
}
/**
* Attach the controller to the view it relates to.
*/

View File

@@ -0,0 +1,24 @@
package com.android.keyguard
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.widget.LinearLayout
class KeyguardStatusContainer(
context: Context,
attrs: AttributeSet,
) : LinearLayout(context, attrs) {
private var drawAlpha: Int = 255
protected override fun onSetAlpha(alpha: Int): Boolean {
drawAlpha = alpha
return true
}
protected override fun dispatchDraw(canvas: Canvas) {
val restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, drawAlpha)
super.dispatchDraw(canvas)
canvas.restoreToCount(restoreTo)
}
}

View File

@@ -19,6 +19,7 @@ package com.android.keyguard;
import static java.util.Collections.emptySet;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Trace;
import android.util.AttributeSet;
@@ -47,6 +48,7 @@ public class KeyguardStatusView extends GridLayout {
private KeyguardSliceView mKeyguardSlice;
private View mMediaHostContainer;
private int mDrawAlpha = 255;
private float mDarkAmount = 0;
public KeyguardStatusView(Context context) {
@@ -136,30 +138,16 @@ public class KeyguardStatusView extends GridLayout {
Trace.endSection();
}
/**
* Clock content will be clipped when goes beyond bounds,
* so we setAlpha for all views except clock
*/
public void setAlpha(float alpha, boolean excludeClock) {
if (!excludeClock) {
setAlpha(alpha);
return;
}
if (alpha == 1 || alpha == 0) {
setAlpha(alpha);
}
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child == mStatusViewContainer) {
for (int j = 0; j < mStatusViewContainer.getChildCount(); j++) {
View innerChild = mStatusViewContainer.getChildAt(j);
if (innerChild != mClockView) {
innerChild.setAlpha(alpha);
}
}
} else {
child.setAlpha(alpha);
}
}
@Override
protected boolean onSetAlpha(int alpha) {
mDrawAlpha = alpha;
return true;
}
@Override
protected void dispatchDraw(Canvas canvas) {
int restoreTo = KeyguardClockFrame.saveCanvasAlpha(this, canvas, mDrawAlpha);
super.dispatchDraw(canvas);
canvas.restoreToCount(restoreTo);
}
}

View File

@@ -180,8 +180,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
*/
public void setAlpha(float alpha) {
if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
mView.setAlpha(alpha, true);
mKeyguardClockSwitchController.setAlpha(alpha);
mView.setAlpha(alpha);
}
}

View File

@@ -134,7 +134,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
private KeyguardClockSwitchController mController;
private View mSliceView;
private LinearLayout mStatusArea;
private FakeExecutor mExecutor;
@Before
@@ -196,8 +195,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mSliceView = new View(getContext());
when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
mStatusArea = new LinearLayout(getContext());
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(
new LinearLayout(getContext()));
}
@Test
@@ -402,15 +401,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
assertNull(mController.getClock());
}
@Test
public void testSetAlpha_setClockAlphaForCLockFace() {
mController.onViewAttached();
mController.setAlpha(0.5f);
verify(mLargeClockView).setAlpha(0.5f);
verify(mSmallClockView).setAlpha(0.5f);
assertEquals(0.5f, mStatusArea.getAlpha(), 0.0f);
}
private void verifyAttachment(VerificationMode times) {
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));

View File

@@ -24,46 +24,33 @@ class KeyguardStatusViewTest : SysuiTestCase() {
get() = keyguardStatusView.findViewById(R.id.status_view_media_container)
private val statusViewContainer: ViewGroup
get() = keyguardStatusView.findViewById(R.id.status_view_container)
private val clockView: ViewGroup
get() = keyguardStatusView.findViewById(R.id.keyguard_clock_container)
private val childrenExcludingMedia
get() = statusViewContainer.children.filter { it != mediaView }
@Before
fun setUp() {
keyguardStatusView = LayoutInflater.from(context)
.inflate(R.layout.keyguard_status_view, /* root= */ null) as KeyguardStatusView
keyguardStatusView =
LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, /* root= */ null)
as KeyguardStatusView
}
@Test
fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() {
val translationY = 1234f
keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */true)
keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */ true)
assertThat(mediaView.translationY).isEqualTo(0)
childrenExcludingMedia.forEach {
assertThat(it.translationY).isEqualTo(translationY)
}
childrenExcludingMedia.forEach { assertThat(it.translationY).isEqualTo(translationY) }
}
@Test
fun setChildrenTranslationYIncludeMediaView() {
val translationY = 1234f
keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */false)
keyguardStatusView.setChildrenTranslationY(translationY, /* excludeMedia= */ false)
statusViewContainer.children.forEach {
assertThat(it.translationY).isEqualTo(translationY)
}
}
@Test
fun setAlphaExcludeClock() {
keyguardStatusView.setAlpha(0.5f, /* excludeClock= */true)
assertThat(statusViewContainer.alpha).isNotEqualTo(0.5f)
assertThat(mediaView.alpha).isEqualTo(0.5f)
assertThat(clockView.alpha).isNotEqualTo(0.5f)
statusViewContainer.children.forEach { assertThat(it.translationY).isEqualTo(translationY) }
}
}