Merge "Stop trying to draw a view not attached to the view tree" into sc-dev

This commit is contained in:
Pierre Barbier de Reuille
2021-04-07 20:40:10 +00:00
committed by Android (Google) Code Review
8 changed files with 137 additions and 85 deletions
+21 -6
View File
@@ -17,16 +17,31 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins. Width & height are set at runtime after scaling the preview
image. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
<com.android.launcher3.widget.WidgetCellPreview
android:id="@+id/widget_preview_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:importantForAccessibility="no"
android:layout_marginVertical="8dp" />
android:layout_marginVertical="8dp">
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins. Width & height are set at runtime after scaling the
preview image. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:layout_gravity="fill"/>
<ImageView
android:id="@+id/widget_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/profile_badge_margin"/>
</com.android.launcher3.widget.WidgetCellPreview>
<!-- The name of the widget. -->
<TextView
@@ -17,12 +17,8 @@ package com.android.launcher3.dragndrop;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -36,38 +32,15 @@ public final class AppWidgetHostViewDrawable extends Drawable {
private final LauncherAppWidgetHostView mAppWidgetHostView;
private Paint mPaint = new Paint();
private final Path mClipPath;
private final boolean mWasAttached;
public AppWidgetHostViewDrawable(LauncherAppWidgetHostView appWidgetHostView) {
mAppWidgetHostView = appWidgetHostView;
mWasAttached = appWidgetHostView.isAttachedToWindow();
Path clipPath = null;
if (appWidgetHostView.getClipToOutline()) {
Outline outline = new Outline();
mAppWidgetHostView.getOutlineProvider().getOutline(mAppWidgetHostView, outline);
Rect rect = new Rect();
if (outline.getRect(rect)) {
float radius = outline.getRadius();
clipPath = new Path();
clipPath.addRoundRect(new RectF(rect), radius, radius, Path.Direction.CCW);
}
}
mClipPath = clipPath;
}
@Override
public void draw(Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getIntrinsicWidth(), getIntrinsicHeight(), mPaint);
if (mClipPath != null) {
canvas.clipPath(mClipPath);
}
// If the view was never attached, or is current attached, then draw. Otherwise do not try
// to draw, or we might trigger bugs with items that get drawn while requiring the view to
// be attached.
if (!mWasAttached || mAppWidgetHostView.isAttachedToWindow()) {
mAppWidgetHostView.draw(canvas);
}
mAppWidgetHostView.draw(canvas);
canvas.restoreToCount(saveCount);
}
@@ -19,6 +19,7 @@ import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
@@ -108,7 +109,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
// If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
// we abort the drag.
if (image.getDrawable() == null) {
if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
return false;
}
@@ -116,11 +117,21 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
dragHelper.setRemoteViewsPreview(v.getPreview());
dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(image, loc);
if (image.getDrawable() != null) {
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(image, loc);
dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
} else {
View preview = v.getAppWidgetHostViewPreview();
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(preview, loc);
Rect r = new Rect(0, 0, preview.getWidth(), preview.getHeight());
dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
new Point(loc[0], loc[1]), this, new DragOptions());
}
close(true);
return true;
}
@@ -115,6 +115,7 @@ public class PendingItemDragHelper extends DragPreviewProvider {
}
if (mAppWidgetHostViewPreview != null) {
preview = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
launcher.getDragController()
.addDragListener(new AppWidgetHostViewDragListener(launcher));
}
@@ -28,9 +28,11 @@ import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -42,7 +44,6 @@ import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -77,7 +78,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
private int mCellSize;
private float mPreviewScale = 1f;
private FrameLayout mWidgetImageContainer;
private WidgetImageView mWidgetImage;
private ImageView mWidgetBadge;
private TextView mWidgetName;
private TextView mWidgetDims;
private TextView mWidgetDescription;
@@ -133,7 +136,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
protected void onFinishInflate() {
super.onFinishInflate();
mWidgetImageContainer = findViewById(R.id.widget_preview_container);
mWidgetImage = findViewById(R.id.widget_preview);
mWidgetBadge = findViewById(R.id.widget_badge);
mWidgetName = findViewById(R.id.widget_name);
mWidgetDims = findViewById(R.id.widget_dims);
mWidgetDescription = findViewById(R.id.widget_description);
@@ -155,7 +160,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
Log.d(TAG, "reset called on:" + mWidgetName.getText());
}
mWidgetImage.animate().cancel();
mWidgetImage.setDrawable(null, null);
mWidgetImage.setDrawable(null);
mWidgetImage.setVisibility(View.VISIBLE);
mWidgetBadge.setImageDrawable(null);
mWidgetName.setText(null);
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
@@ -167,6 +174,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
mActiveRequest = null;
}
mPreview = null;
if (mAppWidgetHostViewPreview != null) {
mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
}
mAppWidgetHostViewPreview = null;
}
@@ -215,6 +225,13 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
mAppWidgetHostViewPreview.setPadding(/* left= */ 0, /* top= */0, /* right= */
0, /* bottom= */ 0);
mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ null);
// Gravity 77 = "fill"
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, /* gravity= */ 77);
mAppWidgetHostViewPreview.setLayoutParams(params);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
mWidgetImage.setVisibility(View.GONE);
}
}
@@ -258,21 +275,36 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
return;
}
if (drawable != null) {
LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
layoutParams.width = (int) (drawable.getIntrinsicWidth() * mPreviewScale);
layoutParams.height = (int) (drawable.getIntrinsicHeight() * mPreviewScale);
mWidgetImage.setLayoutParams(layoutParams);
mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
if (mAnimatePreview) {
mWidgetImage.setAlpha(0f);
ViewPropertyAnimator anim = mWidgetImage.animate();
anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
} else {
mWidgetImage.setAlpha(1f);
setContainerSize(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
mWidgetImage.setDrawable(drawable);
mWidgetImage.setVisibility(View.VISIBLE);
if (mAppWidgetHostViewPreview != null) {
removeView(mAppWidgetHostViewPreview);
mAppWidgetHostViewPreview = null;
}
}
Drawable badge = mWidgetPreviewLoader.getBadgeForUser(mItem.user,
BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx));
if (badge == null) {
mWidgetBadge.setVisibility(View.GONE);
} else {
mWidgetBadge.setVisibility(View.VISIBLE);
mWidgetBadge.setImageDrawable(badge);
}
if (mAnimatePreview) {
mWidgetImageContainer.setAlpha(0f);
ViewPropertyAnimator anim = mWidgetImageContainer.animate();
anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
} else {
mWidgetImageContainer.setAlpha(1f);
}
}
private void setContainerSize(int width, int height) {
LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
layoutParams.width = (int) (width * mPreviewScale);
layoutParams.height = (int) (height * mPreviewScale);
mWidgetImageContainer.setLayoutParams(layoutParams);
}
public void ensurePreview() {
@@ -290,15 +322,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
int viewWidth = dp.cellWidthPx * mItem.spanX;
int viewHeight = dp.cellHeightPx * mItem.spanY;
mAppWidgetHostViewPreview.measure(
MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
viewWidth = mAppWidgetHostViewPreview.getMeasuredWidth();
viewHeight = mAppWidgetHostViewPreview.getMeasuredHeight();
mAppWidgetHostViewPreview.layout(0, 0, viewWidth, viewHeight);
Drawable drawable = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
applyPreview(drawable);
setContainerSize(viewWidth, viewHeight);
applyPreview((Drawable) null);
return;
}
if (mActiveRequest != null) {
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* View group managing the widget preview: either using a {@link WidgetImageView} or an actual
* {@link LauncherAppWidgetHostView}.
*/
public class WidgetCellPreview extends FrameLayout {
public WidgetCellPreview(Context context) {
this(context, null);
}
public WidgetCellPreview(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WidgetCellPreview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
return true;
}
}
@@ -25,7 +25,6 @@ import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
/**
* View that draws a bitmap horizontally centered. If the image width is greater than the view
@@ -37,7 +36,6 @@ public class WidgetImageView extends View {
private final int mBadgeMargin;
private Drawable mDrawable;
private Drawable mBadge;
public WidgetImageView(Context context) {
this(context, null);
@@ -54,9 +52,9 @@ public class WidgetImageView extends View {
.getDimensionPixelSize(R.dimen.profile_badge_margin);
}
public void setDrawable(Drawable drawable, Drawable badge) {
/** Set the drawable to use for this view. */
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
mBadge = badge;
invalidate();
}
@@ -70,11 +68,6 @@ public class WidgetImageView extends View {
updateDstRectF();
mDrawable.setBounds(getBitmapBounds());
mDrawable.draw(canvas);
// Only draw the badge if a preview was drawn.
if (mBadge != null) {
mBadge.draw(canvas);
}
}
}
@@ -105,17 +98,6 @@ public class WidgetImageView extends View {
mDstRectF.top = (myHeight - scaledHeight) / 2;
mDstRectF.bottom = (myHeight + scaledHeight) / 2;
}
if (mBadge != null) {
Rect bounds = mBadge.getBounds();
int left = Utilities.boundToRange(
(int) (mDstRectF.right + mBadgeMargin - bounds.width()),
mBadgeMargin, getWidth() - bounds.width());
int top = Utilities.boundToRange(
(int) (mDstRectF.bottom + mBadgeMargin - bounds.height()),
mBadgeMargin, getHeight() - bounds.height());
mBadge.setBounds(left, top, bounds.width() + left, bounds.height() + top);
}
}
/**
@@ -31,7 +31,6 @@ import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -170,7 +169,7 @@ public final class WidgetsListTableViewHolderBinder
WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
R.layout.widget_cell, tableRow, false);
// set up touch.
WidgetImageView preview = widget.findViewById(R.id.widget_preview);
View preview = widget.findViewById(R.id.widget_preview_container);
preview.setOnClickListener(mIconClickListener);
preview.setOnLongClickListener(mIconLongClickListener);
tableRow.addView(widget);