Load widgets in wallpaper app launcher preview

Test: Widgets in wallpaper app launcher preview rendered
Bug: 185306338
Change-Id: I38569d2ff0b64ba55eb188afa42fba4100aae7ff
This commit is contained in:
Brian Isganitis
2021-06-17 19:56:30 -04:00
parent 485796e814
commit c79d577f18
3 changed files with 170 additions and 90 deletions

View File

@@ -26,6 +26,7 @@ import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially
import android.annotation.TargetApi;
import android.app.Fragment;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
@@ -83,6 +84,8 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -202,6 +205,7 @@ public class LauncherPreviewRenderer extends ContextWrapper
private final InsettableFrameLayout mRootView;
private final Hotseat mHotseat;
private final CellLayout mWorkspace;
private final AppWidgetHost mAppWidgetHost;
public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp) {
super(context);
@@ -255,6 +259,10 @@ public class LauncherPreviewRenderer extends ContextWrapper
mDp.workspacePadding.top,
mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.bottom);
mAppWidgetHost = FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()
? new LauncherPreviewAppWidgetHost(context)
: null;
}
/** Populate preview and render it. */
@@ -354,14 +362,20 @@ public class LauncherPreviewRenderer extends ContextWrapper
private void inflateAndAddWidgets(
LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
AppWidgetHostView view = new NavigableAppWidgetHostView(this) {
@Override
protected boolean shouldAllowDirectClick() {
return false;
}
};
view.setAppWidget(-1, providerInfo);
view.updateAppWidget(null);
AppWidgetHostView view;
if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
view = mAppWidgetHost.createView(mContext, info.appWidgetId, providerInfo);
} else {
view = new NavigableAppWidgetHostView(this) {
@Override
protected boolean shouldAllowDirectClick() {
return false;
}
};
view.setAppWidget(-1, providerInfo);
view.updateAppWidget(null);
}
view.setTag(info);
addInScreenFromBind(view, info);
}
@@ -477,4 +491,31 @@ public class LauncherPreviewRenderer extends ContextWrapper
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
}
private class LauncherPreviewAppWidgetHost extends AppWidgetHost {
private LauncherPreviewAppWidgetHost(Context context) {
super(context, LauncherAppWidgetHost.APPWIDGET_HOST_ID);
}
@Override
protected AppWidgetHostView onCreateView(
Context context,
int appWidgetId,
AppWidgetProviderInfo appWidget) {
return new LauncherPreviewAppWidgetHostView(LauncherPreviewRenderer.this);
}
}
private static class LauncherPreviewAppWidgetHostView extends BaseLauncherAppWidgetHostView {
private LauncherPreviewAppWidgetHostView(Context context) {
super(context);
}
@Override
protected boolean shouldAllowDirectClick() {
return false;
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.graphics.Outline;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.RemoteViews;
import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.util.Executors;
/**
* Launcher AppWidgetHostView with support for rounded corners and a fallback View.
*/
public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHostView {
protected final LayoutInflater mInflater;
private final Rect mEnforcedRectangle = new Rect();
private final float mEnforcedCornerRadius;
private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
outline.setEmpty();
} else {
outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
}
}
};
public BaseLauncherAppWidgetHostView(Context context) {
super(context);
setExecutor(Executors.THREAD_POOL_EXECUTOR);
mInflater = LayoutInflater.from(context);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
}
@Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
/**
* Fall back to error layout instead of showing widget.
*/
public void switchToErrorView() {
// Update the widget with 0 Layout id, to reset the view to error view.
updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
post(this::switchToErrorView);
}
enforceRoundedCorners();
}
@UiThread
private void resetRoundedCorners() {
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
setClipToOutline(false);
}
@UiThread
private void enforceRoundedCorners() {
if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
resetRoundedCorners();
return;
}
View background = RoundedCornerEnforcement.findBackground(this);
if (background == null
|| RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
resetRoundedCorners();
return;
}
RoundedCornerEnforcement.computeRoundedRectangle(this,
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
setClipToOutline(true);
}
/** Returns the corner radius currently enforced, in pixels. */
public float getEnforcedCornerRadius() {
return mEnforcedCornerRadius;
}
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
return getClipToOutline();
}
}

View File

@@ -20,19 +20,16 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.Advanceable;
@@ -40,7 +37,6 @@ import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Launcher;
@@ -51,7 +47,6 @@ import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
@@ -61,7 +56,7 @@ import java.util.List;
/**
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
implements TouchCompleteListener, View.OnLongClickListener,
LocalColorExtractor.Listener {
@@ -76,8 +71,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
// Maximum duration for which updates can be deferred.
private static final long UPDATE_LOCK_TIMEOUT_MILLIS = 1000;
protected final LayoutInflater mInflater;
private final CheckLongPressHelper mLongPressHelper;
protected final Launcher mLauncher;
private final Workspace mWorkspace;
@@ -101,18 +94,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
private final Rect mWidgetSizeAtDrag = new Rect();
private final RectF mTempRectF = new RectF();
private final Rect mEnforcedRectangle = new Rect();
private final float mEnforcedCornerRadius;
private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
outline.setEmpty();
} else {
outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
}
}
};
private final Object mUpdateLock = new Object();
private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
private long mDeferUpdatesUntilMillis = 0;
@@ -123,18 +104,15 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
mLauncher = Launcher.getLauncher(context);
mWorkspace = mLauncher.getWorkspace();
mLongPressHelper = new CheckLongPressHelper(this, this);
mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
setExecutor(Executors.THREAD_POOL_EXECUTOR);
if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
}
mColorExtractor = LocalColorExtractor.newInstance(getContext());
mColorExtractor.setListener(this);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
}
@@ -165,11 +143,6 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
return true;
}
@Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
@Override
public void updateAppWidget(RemoteViews remoteViews) {
synchronized (mUpdateLock) {
@@ -304,34 +277,17 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
}
}
public void switchToErrorView() {
// Update the widget with 0 Layout id, to reset the view to error view.
updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
post(new Runnable() {
@Override
public void run() {
switchToErrorView();
}
});
}
super.onLayout(changed, left, top, right, bottom);
mIsScrollable = checkScrollableRecursively(this);
if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
mDragLayerRelativeCoordinateHelper.viewToRect(this, mCurrentWidgetSize);
updateColorExtraction(mCurrentWidgetSize,
mWorkspace.getPageIndexForScreenId(info.screenId));
}
enforceRoundedCorners();
}
/** Starts the drag mode. */
@@ -502,40 +458,4 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
}
return false;
}
@UiThread
private void resetRoundedCorners() {
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
setClipToOutline(false);
}
@UiThread
private void enforceRoundedCorners() {
if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
resetRoundedCorners();
return;
}
View background = RoundedCornerEnforcement.findBackground(this);
if (background == null
|| RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
resetRoundedCorners();
return;
}
RoundedCornerEnforcement.computeRoundedRectangle(this,
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
setClipToOutline(true);
}
/** Returns the corner radius currently enforced, in pixels. */
public float getEnforcedCornerRadius() {
return mEnforcedCornerRadius;
}
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
return getClipToOutline();
}
}