Avoid flicker to drop a widget that needs a config activity.
When dropping a widget that requires an config activity, drop a PendingAppWidgetHostView to workspace Adb command to try the feature: adb shell device_config put launcher com.android.launcher3.enable_add_app_widget_via_config_activity_v2 true Fix: 284236964 Test: manual Flag: aconfig launcher.enable_add_app_widget_via_config_activity_v2 DISABLED Change-Id: Ifd0be5c607a388cf8a8f6d77b46c03112e3e599f
This commit is contained in:
@@ -83,6 +83,9 @@ flag {
|
||||
namespace: "launcher"
|
||||
description: "Enables full width two pane widget picker for tablets in landscape and portrait"
|
||||
bug: "315055849"
|
||||
metadata {
|
||||
purpose: PURPOSE_BUGFIX
|
||||
}
|
||||
}
|
||||
|
||||
flag {
|
||||
@@ -156,6 +159,13 @@ flag {
|
||||
bug: "318410881"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "enable_add_app_widget_via_config_activity_v2"
|
||||
namespace: "launcher"
|
||||
description: "When adding app widget through config activity, directly add it to workspace to reduce flicker"
|
||||
bug: "284236964"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "use_activity_overlay"
|
||||
namespace: "launcher"
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
|
||||
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
|
||||
import static com.android.launcher3.Flags.enableAddAppWidgetViaConfigActivityV2;
|
||||
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
||||
@@ -116,6 +117,8 @@ import android.content.IntentSender;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
@@ -829,7 +832,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
announceForAccessibility(R.string.item_added_to_workspace);
|
||||
break;
|
||||
case REQUEST_CREATE_APPWIDGET:
|
||||
completeAddAppWidget(appWidgetId, info, null, null);
|
||||
completeAddAppWidget(appWidgetId, info, null, null, false, null);
|
||||
break;
|
||||
case REQUEST_RECONFIGURE_APPWIDGET:
|
||||
getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
|
||||
@@ -1016,11 +1019,18 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
AppWidgetHostView boundWidget = null;
|
||||
if (resultCode == RESULT_OK) {
|
||||
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
|
||||
final AppWidgetHostView layout = mAppWidgetHolder.createView(appWidgetId,
|
||||
requestArgs.getWidgetHandler().getProviderInfo(this));
|
||||
|
||||
// Now that we are exiting the config activity with RESULT_OK.
|
||||
// If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we can retrieve the
|
||||
// PendingAppWidgetHostView from LauncherWidgetHolder (it was added to
|
||||
// LauncherWidgetHolder when starting the config activity).
|
||||
final AppWidgetHostView layout = enableAddAppWidgetViaConfigActivityV2()
|
||||
? getWorkspace().getWidgetForAppWidgetId(appWidgetId)
|
||||
: mAppWidgetHolder.createView(appWidgetId,
|
||||
requestArgs.getWidgetHandler().getProviderInfo(this));
|
||||
boundWidget = layout;
|
||||
onCompleteRunnable = () -> {
|
||||
completeAddAppWidget(appWidgetId, requestArgs, layout, null);
|
||||
completeAddAppWidget(appWidgetId, requestArgs, layout, null, false, null);
|
||||
if (!isInState(EDIT_MODE)) {
|
||||
mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
|
||||
}
|
||||
@@ -1450,14 +1460,15 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
*/
|
||||
@Thunk
|
||||
void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
|
||||
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
|
||||
@Nullable AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo,
|
||||
boolean showPendingWidget, @Nullable Bitmap widgetPreviewBitmap) {
|
||||
|
||||
if (appWidgetInfo == null) {
|
||||
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId,
|
||||
itemInfo.getTargetComponent());
|
||||
}
|
||||
|
||||
if (hostView == null) {
|
||||
if (hostView == null && !showPendingWidget) {
|
||||
// Perform actual inflation because we're live
|
||||
hostView = mAppWidgetHolder.createView(appWidgetId, appWidgetInfo);
|
||||
}
|
||||
@@ -1471,39 +1482,71 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
launcherInfo.minSpanX = itemInfo.minSpanX;
|
||||
launcherInfo.minSpanY = itemInfo.minSpanY;
|
||||
launcherInfo.user = appWidgetInfo.getProfile();
|
||||
CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo);
|
||||
if (showPendingWidget) {
|
||||
launcherInfo.restoreStatus = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
|
||||
PendingAppWidgetHostView pendingAppWidgetHostView =
|
||||
new PendingAppWidgetHostView(this, launcherInfo, appWidgetInfo);
|
||||
pendingAppWidgetHostView.setPreviewBitmap(widgetPreviewBitmap);
|
||||
hostView = pendingAppWidgetHostView;
|
||||
} else if (hostView instanceof PendingAppWidgetHostView) {
|
||||
((PendingAppWidgetHostView) hostView).setPreviewBitmap(null);
|
||||
// User has selected a widget config and exited the config activity, we can trigger
|
||||
// re-inflation of PendingAppWidgetHostView to replace it with
|
||||
// LauncherAppWidgetHostView in workspace.
|
||||
completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
|
||||
|
||||
// Show resize frame on the newly inflated LauncherAppWidgetHostView.
|
||||
LauncherAppWidgetHostView reInflatedHostView =
|
||||
getWorkspace().getWidgetForAppWidgetId(appWidgetId);
|
||||
showWidgetResizeFrame(
|
||||
reInflatedHostView,
|
||||
(LauncherAppWidgetInfo) reInflatedHostView.getTag(),
|
||||
presenterPos);
|
||||
return;
|
||||
}
|
||||
if (itemInfo instanceof PendingAddWidgetInfo) {
|
||||
launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer;
|
||||
} else if (itemInfo instanceof PendingRequestArgs) {
|
||||
launcherInfo.sourceContainer =
|
||||
((PendingRequestArgs) itemInfo).getWidgetSourceContainer();
|
||||
}
|
||||
CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo);
|
||||
getModelWriter().addItemToDatabase(launcherInfo,
|
||||
itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY);
|
||||
|
||||
hostView.setVisibility(View.VISIBLE);
|
||||
mItemInflater.prepareAppWidget(hostView, launcherInfo);
|
||||
mWorkspace.addInScreen(hostView, launcherInfo);
|
||||
if (!enableAddAppWidgetViaConfigActivityV2() || hostView.getParent() == null) {
|
||||
mWorkspace.addInScreen(hostView, launcherInfo);
|
||||
}
|
||||
announceForAccessibility(R.string.item_added_to_workspace);
|
||||
|
||||
// Show the widget resize frame.
|
||||
if (hostView instanceof LauncherAppWidgetHostView) {
|
||||
final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView;
|
||||
CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId);
|
||||
if (mStateManager.getState() == NORMAL) {
|
||||
AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
|
||||
} else {
|
||||
mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState finalState) {
|
||||
if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE)
|
||||
&& finalState == NORMAL) {
|
||||
AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
|
||||
mStateManager.removeStateListener(this);
|
||||
}
|
||||
showWidgetResizeFrame(launcherHostView, launcherInfo, presenterPos);
|
||||
}
|
||||
}
|
||||
|
||||
/** Show widget resize frame. */
|
||||
private void showWidgetResizeFrame(
|
||||
LauncherAppWidgetHostView launcherHostView,
|
||||
LauncherAppWidgetInfo launcherInfo,
|
||||
CellPos presenterPos) {
|
||||
CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId);
|
||||
if (mStateManager.getState() == NORMAL) {
|
||||
AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
|
||||
} else {
|
||||
mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState finalState) {
|
||||
if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE)
|
||||
&& finalState == NORMAL) {
|
||||
AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
|
||||
mStateManager.removeStateListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1760,19 +1803,39 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled, we always add widget
|
||||
* host view to workspace, otherwise we only add widget to host view if config activity is
|
||||
* not started.
|
||||
*/
|
||||
void addAppWidgetImpl(int appWidgetId, ItemInfo info,
|
||||
AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) {
|
||||
if (!addFlowHandler.startConfigActivity(this, appWidgetId, info,
|
||||
REQUEST_CREATE_APPWIDGET)) {
|
||||
// If the configuration flow was not started, add the widget
|
||||
final boolean isActivityStarted = addFlowHandler.startConfigActivity(
|
||||
this, appWidgetId, info, REQUEST_CREATE_APPWIDGET);
|
||||
|
||||
// Exit spring loaded mode if necessary after adding the widget
|
||||
Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null
|
||||
: () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
|
||||
completeAddAppWidget(appWidgetId, info, boundWidget,
|
||||
addFlowHandler.getProviderInfo(this));
|
||||
mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
|
||||
if (!enableAddAppWidgetViaConfigActivityV2() && isActivityStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If FLAG_ENABLE_ADD_APP_WIDGET_VIA_CONFIG_ACTIVITY_V2 is enabled and config activity is
|
||||
// started, we should remove the dropped AppWidgetHostView from drag layer and extract the
|
||||
// Bitmap that shows the preview. Then pass the Bitmap to completeAddAppWidget() to create
|
||||
// a PendingWidgetHostView.
|
||||
Bitmap widgetPreviewBitmap = null;
|
||||
if (isActivityStarted) {
|
||||
DragView dropView = getDragLayer().clearAnimatedView();
|
||||
if (dropView != null && dropView.containsAppWidgetHostView()) {
|
||||
widgetPreviewBitmap = getBitmapFromView(dropView.getContentView());
|
||||
}
|
||||
}
|
||||
|
||||
// Exit spring loaded mode if necessary after adding the widget
|
||||
Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null
|
||||
: () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
|
||||
completeAddAppWidget(appWidgetId, info, boundWidget,
|
||||
addFlowHandler.getProviderInfo(this), addFlowHandler.needsConfigure(),
|
||||
widgetPreviewBitmap);
|
||||
mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
|
||||
}
|
||||
|
||||
public void addPendingItem(PendingAddItemInfo info, int container, int screenId,
|
||||
@@ -2373,6 +2436,18 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Convert a {@link View} to {@link Bitmap}. */
|
||||
private static Bitmap getBitmapFromView(@Nullable View view) {
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
Bitmap returnedBitmap =
|
||||
Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(returnedBitmap);
|
||||
view.draw(canvas);
|
||||
return returnedBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first view matching the operator in the given ViewGroups, or null if none.
|
||||
* Forward iteration matters.
|
||||
|
||||
@@ -2988,7 +2988,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
}
|
||||
|
||||
public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
|
||||
final Runnable onCompleteRunnable, int animationType, final View finalView,
|
||||
final Runnable onCompleteRunnable, int animationType, @Nullable final View finalView,
|
||||
boolean external) {
|
||||
int[] finalPos = new int[2];
|
||||
float scaleXY[] = new float[2];
|
||||
|
||||
@@ -42,6 +42,8 @@ import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.app.animation.Interpolators;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.DropTargetBar;
|
||||
@@ -388,7 +390,13 @@ public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverla
|
||||
mDropAnim.start();
|
||||
}
|
||||
|
||||
public void clearAnimatedView() {
|
||||
/**
|
||||
* Remove the drop view and end the drag animation.
|
||||
*
|
||||
* @return {@link DragView} that is removed.
|
||||
*/
|
||||
@Nullable
|
||||
public DragView clearAnimatedView() {
|
||||
if (mDropAnim != null) {
|
||||
mDropAnim.cancel();
|
||||
}
|
||||
@@ -396,8 +404,10 @@ public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverla
|
||||
if (mDropView != null) {
|
||||
mDragController.onDeferredEndDrag(mDropView);
|
||||
}
|
||||
DragView ret = mDropView;
|
||||
mDropView = null;
|
||||
invalidate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public View getAnimatedView() {
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.annotation.TargetApi;
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
@@ -563,6 +564,11 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
|
||||
return mContentViewParent;
|
||||
}
|
||||
|
||||
/** Return true if {@link mContent} is a {@link AppWidgetHostView}. */
|
||||
public boolean containsAppWidgetHostView() {
|
||||
return mContent instanceof AppWidgetHostView;
|
||||
}
|
||||
|
||||
private static class SpringFloatValue {
|
||||
|
||||
private static final FloatPropertyCompat<SpringFloatValue> VALUE =
|
||||
|
||||
@@ -16,13 +16,20 @@
|
||||
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||
import static android.graphics.Paint.DITHER_FLAG;
|
||||
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||
|
||||
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
|
||||
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
@@ -85,8 +92,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||
private boolean mDrawableSizeChanged;
|
||||
|
||||
private final TextPaint mPaint;
|
||||
|
||||
private final Paint mPreviewPaint;
|
||||
private Layout mSetupTextLayout;
|
||||
|
||||
@Nullable private Bitmap mPreviewBitmap;
|
||||
|
||||
public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
|
||||
@Nullable LauncherAppWidgetProviderInfo appWidget) {
|
||||
this(context, info, appWidget,
|
||||
@@ -116,6 +127,15 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||
mDrawableSizeChanged = true;
|
||||
}
|
||||
|
||||
/** Set {@link Bitmap} of widget preview. */
|
||||
public void setPreviewBitmap(@Nullable Bitmap previewBitmap) {
|
||||
if (this.mPreviewBitmap == previewBitmap) {
|
||||
return;
|
||||
}
|
||||
this.mPreviewBitmap = previewBitmap;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
|
||||
LauncherAppWidgetProviderInfo appwidget, CharSequence label) {
|
||||
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
|
||||
@@ -130,11 +150,16 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||
mPaint.setColor(Themes.getAttrColor(getContext(), android.R.attr.textColorPrimary));
|
||||
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
|
||||
mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
|
||||
|
||||
mPreviewPaint = new Paint(ANTI_ALIAS_FLAG | DITHER_FLAG | FILTER_BITMAP_FLAG);
|
||||
setWillNotDraw(false);
|
||||
setBackgroundResource(R.drawable.pending_widget_bg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppWidgetProviderInfo getAppWidgetInfo() {
|
||||
return mAppwidget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAppWidget(RemoteViews remoteViews) {
|
||||
checkIfRestored();
|
||||
@@ -393,6 +418,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mPreviewBitmap != null
|
||||
&& (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0) {
|
||||
canvas.drawBitmap(mPreviewBitmap, 0, 0, mPreviewPaint);
|
||||
return;
|
||||
}
|
||||
if (mCenterDrawable == null) {
|
||||
// Nothing to draw
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user