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:
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user