Android Q的锁屏左右下角各有一个90度的直角,本来也没注意,但是用着用着发现这两个view会消失。便去追踪了下这个直角的来源。
首先很自然的先到这个view应该跟P上水滴的ScreenDecorations,这样一个屏幕装饰器相关。在该类下查找其加载的R.layout.rounded_corners,看到有个叫CornerHandleView的,角落的view,看名字比较像,该类的onDraw方法中主要是再canvas上绘制一个path路径,path的赋值如下:
private void updatePath() {
mPath = new Path();
float marginPx = getMarginPx();
float radiusPx = getInnerRadiusPx();
float halfStrokePx = getStrokePx() / 2f;
float angle = getAngle();
float startAngle = 180 + ((90 - angle) / 2);
RectF circle = new RectF(marginPx + halfStrokePx,
marginPx + halfStrokePx,
marginPx + 2 * radiusPx - halfStrokePx,
marginPx + 2 * radiusPx - halfStrokePx);
if (angle >= 90f) {
float innerCircumferenceDp = convertPixelToDp(radiusPx * 2 * (float) Math.PI,
mContext);
float arcDp = innerCircumferenceDp * getAngle() / 360f;
// Add additional "arms" to the two ends of the arc. The length computation is
// hand-tuned.
float lineLengthPx = convertDpToPixel((ARC_LENGTH_DP - arcDp - MARGIN_DP) / 2,
mContext);
mPath.moveTo(marginPx + halfStrokePx, marginPx + radiusPx + lineLengthPx);
mPath.lineTo(marginPx + halfStrokePx, marginPx + radiusPx);
mPath.arcTo(circle, startAngle, angle);
mPath.moveTo(marginPx + radiusPx, marginPx + halfStrokePx);
mPath.lineTo(marginPx + radiusPx + lineLengthPx, marginPx + halfStrokePx);
} else {
mPath.arcTo(circle, startAngle, angle);
}
}
mPath.arcTo是绘制一段弧线,if中显然是线绘制一条竖线,在绘制一段弧线,在绘制一条横线。显然就与效果图比较贴近了。在追踪该类下的相关读取dimen中的数值,都是跟这个直角的角度相关的。
在来看下是谁在控制这个CornerHandleView的显示/隐藏的
很直接的在ScreenDecorations中有一个
private void updateWindowVisibility(View overlay) {
boolean visibleForCutout = shouldDrawCutout()
&& overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
boolean visibleForRoundedCorners = hasRoundedCorners();
boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility()
== View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility()
== View.VISIBLE;
overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles
? View.VISIBLE : View.GONE);
}
设置整个layout的Visibility,打log发现者3个条件都是true,看来不是在这里。
在看setAssistHintVisible()
这里直接对这个CornerHandleView操作了,fade方法。
private void fade(View view, boolean fadeIn, boolean isLeft) {
Log.e("cyy","fade################:"+fadeIn);
if (fadeIn) {
view.animate().cancel();
view.setAlpha(1f);
view.setVisibility(View.VISIBLE);
// A piecewise spring-like interpolation.
// End value in one animator call must match the start value in the next, otherwise
// there will be a discontinuity.
AnimatorSet anim = new AnimatorSet();
Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750,
new PathInterpolator(0, 0.45f, .67f, 1f));
Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f);
Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400,
secondInterpolator);
Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400,
secondInterpolator);
Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400,
secondInterpolator);
anim.play(first).before(second);
anim.play(second).before(third);
anim.play(third).before(fourth);
anim.start();
} else {
view.animate().cancel();
view.animate()
.setInterpolator(new AccelerateInterpolator(1.5f))
.setDuration(250)
.alpha(0f);
}
}
这个fade方法:if是做了个动画,else设置了alpha为0,就是隐藏了。
怀疑就是这里处理的,public void setAssistHintVisible(boolean visible)是public类型的,就可以被外部调用,这里添加trace查看调用路径。
流程转到了AssistHandleBehaviorController中,该类的内容也不多,
@Override // AssistHandleCallbacks
public void hide() {
clearPendingCommands();
mHandler.post(mHideHandles);
}
@Override // AssistHandleCallbacks
public void showAndGo() {
clearPendingCommands();
mHandler.post(mShowAndGo);
}
private void showAndGoInternal() {
maybeShowHandles(/* ignoreThreshold = */ false);
mHandler.postDelayed(mHideHandles, getShowAndGoDuration());
}
@Override // AssistHandleCallbacks
public void showAndGoDelayed(long delayMs, boolean hideIfShowing) {
clearPendingCommands();
if (hideIfShowing) {
mHandler.post(mHideHandles);
}
mHandler.postDelayed(mShowAndGo, delayMs);
}
@Override // AssistHandleCallbacks
public void showAndStay() {
clearPendingCommands();
mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
}
这些以show开头的方法中,前3个都是调到了showAndGoInternal(),最终调到了hide的,是隐藏CornerHandleVIew的,最后一个showAndStay
private void maybeShowHandles(boolean ignoreThreshold) {
if (mHandlesShowing) {
return;
}
if (handlesUnblocked(ignoreThreshold)) {
ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
if (screenDecorations == null) {
Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
} else {
mHandlesShowing = true;
screenDecorations.setAssistHintVisible(true);
}
}
}
是screenDecorations.setAssistHintVisible(true);即显示CornerHandleView的。
手机灭下屏,绘制锁屏,同时CornerHandleView会被绘制出来
复现灭屏在亮屏时,CornerHandleView不显示的情况,查看trace是断在了哪里。
追踪到是AssistHandleReminderExpBehavior.java下的一个mIsLearned的变量发生了变化。
mIsLearned的赋值是通过上面的updateLearningStatus来的,当mLearningCount > 设定的固定值是,mIsLearned值变化,导致后面的调用逻辑变化,而mLearningCount 是在每次成功调起google 助手时就会被+1。
@Override
public void onAssistantGesturePerformed() {
if (mContext == null) {
return;
}
Settings.Secure.putLong(
mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount);
}
同时查看google pixel机器,发现恢复出厂设置后操作10次也是锁屏上的CornerHandleView也会消失,因此得出,该现象是google的原生设计,ConnerHandleVIew就是给新用户的一个提示,告之用户在锁屏上,从最左边/右边滑动,会触发google serach,当用户成功触发该操作10次,就认为用户已经learned,就不在显示该view。
Q上还有很多新特性,有待发现。