Merge "Added assistant gesture swipe up right side region on nav bar" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index afc8e47..47e0e61 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -19,14 +19,12 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import android.graphics.Rect;
-
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.IconRecentsView;
/**
* Definition for overview state
@@ -50,6 +48,12 @@
}
@Override
+ public void onStateEnabled(Launcher launcher) {
+ IconRecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.onBeginTransitionToOverview();
+ }
+
+ @Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
return new PageAlphaProvider(DEACCEL_2) {
@Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 77c3f33..57cd60a 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -33,10 +33,10 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
- private final ArrayList<Task> mTaskList;
+ private final TaskListLoader mLoader;
- public TaskAdapter(@NonNull ArrayList<Task> taskList) {
- mTaskList = taskList;
+ public TaskAdapter(@NonNull TaskListLoader loader) {
+ mLoader = loader;
}
@Override
@@ -48,11 +48,16 @@
@Override
public void onBindViewHolder(TaskHolder holder, int position) {
- holder.bindTask(mTaskList.get(position));
+ ArrayList<Task> tasks = mLoader.getCurrentTaskList();
+ if (position >= tasks.size()) {
+ // Task list has updated.
+ return;
+ }
+ holder.bindTask(tasks.get(position));
}
@Override
public int getItemCount() {
- return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY);
+ return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
}
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
new file mode 100644
index 0000000..9f359b4
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+/**
+ * This class is responsible for maintaining the list of tasks and the task content. The list must
+ * be updated explicitly with {@link #loadTaskList} whenever the list needs to be
+ * up-to-date.
+ */
+public final class TaskListLoader {
+
+ private final RecentsModel mRecentsModel;
+
+ private ArrayList<Task> mTaskList = new ArrayList<>();
+ private int mTaskListChangeId;
+
+ public TaskListLoader(Context context) {
+ mRecentsModel = RecentsModel.INSTANCE.get(context);
+ }
+
+ /**
+ * Returns the current task list as of the last completed load (see
+ * {@link #loadTaskList}). This list of tasks is guaranteed to always have all its task
+ * content loaded.
+ *
+ * @return the current list of tasks w/ all content loaded
+ */
+ public ArrayList<Task> getCurrentTaskList() {
+ return mTaskList;
+ }
+
+ /**
+ * Fetches the most recent tasks and updates the task list asynchronously. In addition it
+ * loads the content for each task (icon and label). The callback and task list being updated
+ * only occur when all task content is fully loaded and up-to-date.
+ *
+ * @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
+ * thread
+ */
+ public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
+ if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+ // Current task list is already up to date. No need to update.
+ if (onTasksLoadedCallback != null) {
+ onTasksLoadedCallback.accept(mTaskList);
+ }
+ return;
+ }
+ // TODO: Look into error checking / more robust handling for when things go wrong.
+ mTaskListChangeId = mRecentsModel.getTasks(tasks -> {
+ // Reverse tasks to put most recent at the bottom of the view
+ Collections.reverse(tasks);
+ // Load task content
+ loadTaskContents(tasks, () -> {
+ mTaskList = tasks;
+ if (onTasksLoadedCallback != null) {
+ onTasksLoadedCallback.accept(mTaskList);
+ }
+ });
+ });
+ }
+
+ /**
+ * Loads task content for a list of tasks, including the label and the icon. Uses the list of
+ * tasks since the last load as a cache for loaded content.
+ *
+ * @param tasksToLoad list of tasks that need to load their content
+ * @param onLoadedCallback runnable to run after all tasks have loaded their content
+ */
+ private void loadTaskContents(ArrayList<Task> tasksToLoad,
+ @Nullable Runnable onLoadedCallback) {
+ AtomicInteger loadRequestsCount = new AtomicInteger(0);
+ for (Task task : tasksToLoad) {
+ int index = mTaskList.indexOf(task);
+ if (index >= 0) {
+ // If we've already loaded the task and have its content then just copy it over.
+ Task loadedTask = mTaskList.get(index);
+ task.titleDescription = loadedTask.titleDescription;
+ task.icon = loadedTask.icon;
+ } else {
+ // Otherwise, load the content in the background.
+ loadRequestsCount.getAndIncrement();
+ mRecentsModel.getIconCache().updateIconInBackground(task, loadedTask -> {
+ if (loadRequestsCount.decrementAndGet() == 0 && onLoadedCallback != null) {
+ onLoadedCallback.run();
+ }
+ });
+ }
+ }
+ if (loadRequestsCount.get() == 0 && onLoadedCallback != null) {
+ onLoadedCallback.run();
+ }
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 15da10c..afb0540 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,11 +27,8 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.R;
-import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskAdapter;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
+import com.android.quickstep.TaskListLoader;
/**
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -77,13 +74,12 @@
*/
@ViewDebug.ExportedProperty(category = "launcher")
- // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter.
- private static final ArrayList<Task> DUMMY_TASK_LIST = new ArrayList<>();
private final Context mContext;
private float mTranslationYFactor;
private TaskAdapter mTaskAdapter;
private RecyclerView mTaskRecyclerView;
+ private TaskListLoader mTaskLoader;
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -93,13 +89,30 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST);
+ mTaskLoader = new TaskListLoader(mContext);
+ mTaskAdapter = new TaskAdapter(mTaskLoader);
mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
mTaskRecyclerView.setAdapter(mTaskAdapter);
mTaskRecyclerView.setLayoutManager(
new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
}
+ /**
+ * Logic for when we know we are going to overview/recents and will be putting up the recents
+ * view. This should be used to prepare recents (e.g. load any task data, etc.) before it
+ * becomes visible.
+ *
+ * TODO: Hook this up for fallback recents activity as well
+ */
+ public void onBeginTransitionToOverview() {
+ // Load any task changes
+ mTaskLoader.loadTaskList(tasks -> {
+ // TODO: Put up some loading UI while task content is loading. May have to do something
+ // smarter when animating from app to overview.
+ mTaskAdapter.notifyDataSetChanged();
+ });
+ }
+
public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index ffd3b4b..408b749 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -104,13 +104,11 @@
.sourceComponent;
final View workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
- final FloatingIconView floatingView = workspaceView == null ? null
- : new FloatingIconView(activity);
final Rect iconLocation = new Rect();
- if (floatingView != null) {
- floatingView.matchPositionOf(activity, workspaceView, true /* hideOriginal */,
- iconLocation);
- }
+ final FloatingIconView floatingView = workspaceView == null ? null
+ : FloatingIconView.getFloatingIconView(activity, workspaceView,
+ true /* hideOriginal */, false /* useDrawableAsIs */,
+ activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null);
return new HomeAnimationFactory() {
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 4d0136e..fa827e7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -972,27 +972,36 @@
final RectF currentRect = new RectF();
final View floatingView = homeAnimationFactory.getFloatingView();
+ final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
+
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- if (floatingView instanceof FloatingIconView) {
+ if (isFloatingIconView) {
anim.addListener((FloatingIconView) floatingView);
}
+
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f;
anim.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
- float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
+ float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress);
// Initially go towards original target (task view in recents),
// but accelerate towards the final target.
// TODO: This is technically not correct. Instead, motion should continue at
// the released velocity but accelerate towards the target.
targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
originalTarget, finalTarget));
- currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
- float alpha = 1 - interpolatedProgress;
- mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
+ currentRect.set(rectFEvaluator.evaluate(interpolatedProgress, startRect, targetRect));
+
+ float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
+ windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
+ mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
.setSyncTransactionApplier(mSyncTransactionApplier);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
- if (floatingView instanceof FloatingIconView) {
- ((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
+ if (isFloatingIconView) {
+ ((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress,
+ windowAlphaThreshold);
}
});
anim.addListener(new AnimationSuccessListener() {
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index ab5f479..f8b167b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -403,9 +403,7 @@
private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
- if (mFloatingView == null) {
- mFloatingView = new FloatingIconView(mLauncher);
- } else {
+ if (mFloatingView != null) {
mFloatingView.setTranslationX(0);
mFloatingView.setTranslationY(0);
mFloatingView.setScaleX(1);
@@ -414,7 +412,8 @@
mFloatingView.setBackground(null);
}
Rect rect = new Rect();
- mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
+ mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
+ true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView);
int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
@@ -615,11 +614,6 @@
WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-
- definition.addRemoteAnimation(
- WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(true /* fromUnlock */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 01535b0..3b054c2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -611,6 +611,12 @@
outBounds.right = outBounds.left + (getCellSize().x * spanX);
}
+ public float getAspectRatioWithInsets() {
+ int w = widthPx - mInsets.left - mInsets.right;
+ int h = heightPx - mInsets.top - mInsets.bottom;
+ return ((float) Math.max(w, h)) / Math.min(w, h);
+ }
+
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f571aa3..d65fe76 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -319,6 +319,7 @@
}
}
restoreState(savedInstanceState);
+ mStateManager.reapplyState();
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 832e9c9..c3edfe5 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -37,6 +38,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -55,14 +57,24 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -556,6 +568,7 @@
public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
final DragLayer dragLayer = launcher.getDragLayer();
final boolean isBubbleTextView = v instanceof BubbleTextView;
+ final boolean isFolderIcon = v instanceof FolderIcon;
final Rect rect = new Rect();
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
@@ -563,15 +576,14 @@
// Deep shortcut views have their icon drawn in a separate view.
DeepShortcutView view = (DeepShortcutView) v.getParent();
dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
- } else if (isBubbleTextView && v.getTag() instanceof ItemInfo
+ } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
&& (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
|| ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
- BubbleTextView btv = (BubbleTextView) v;
- CellLayout pageViewIsOn = ((CellLayout) btv.getParent().getParent());
+ CellLayout pageViewIsOn = ((CellLayout) v.getParent().getParent());
int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
DeviceProfile dp = launcher.getDeviceProfile();
- ItemInfo info = ((ItemInfo) btv.getTag());
+ ItemInfo info = ((ItemInfo) v.getTag());
dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
} else {
@@ -595,6 +607,51 @@
public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
try {
context.unregisterReceiver(receiver);
- } catch (IllegalArgumentException e) { }
+ } catch (IllegalArgumentException e) {}
+ }
+
+ /**
+ * Returns the full drawable for {@param info}.
+ * @param outObj this is set to the internal data associated with {@param info},
+ * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+ */
+ public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
+ Object[] outObj) {
+ LauncherAppState appState = LauncherAppState.getInstance(launcher);
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
+ .resolveActivity(info.getIntent(), info.user);
+ outObj[0] = activityInfo;
+ return (activityInfo != null) ? appState.getIconCache()
+ .getFullResIcon(activityInfo, false) : null;
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (info instanceof PendingAddShortcutInfo) {
+ ShortcutConfigActivityInfo activityInfo =
+ ((PendingAddShortcutInfo) info).activityInfo;
+ outObj[0] = activityInfo;
+ return activityInfo.getFullResIcon(appState.getIconCache());
+ }
+ ShortcutKey key = ShortcutKey.fromItemInfo(info);
+ DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
+ List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+ key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ outObj[0] = si.get(0);
+ return sm.getShortcutIconDrawable(si.get(0),
+ appState.getInvariantDeviceProfile().fillResIconDpi);
+ }
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
+ launcher, info.id, new Point(width, height));
+ if (icon == null) {
+ return null;
+ }
+ outObj[0] = icon;
+ return icon;
+ } else {
+ return null;
+ }
}
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1e19685..8c57f46 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -101,6 +101,10 @@
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");
+ public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
+ "ADAPTIVE_ICON_WINDOW_ANIM", false,
+ "Use adaptive icons for window animations.");
+
public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8f223a3..8a27f9d 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,7 +24,6 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
-import android.content.pm.LauncherActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -54,18 +53,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.Arrays;
-import java.util.List;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -204,11 +197,11 @@
public void run() {
LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
Object[] outObj = new Object[1];
- Drawable dr = getFullDrawable(info, appState, outObj);
+ int w = mBitmap.getWidth();
+ int h = mBitmap.getHeight();
+ Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
if (dr instanceof AdaptiveIconDrawable) {
- int w = mBitmap.getWidth();
- int h = mBitmap.getHeight();
int blurMargin = (int) mLauncher.getResources()
.getDimension(R.dimen.blur_size_medium_outline) / 2;
@@ -314,49 +307,6 @@
}
/**
- * Returns the full drawable for {@param info}.
- * @param outObj this is set to the internal data associated with {@param info},
- * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
- */
- private Drawable getFullDrawable(ItemInfo info, LauncherAppState appState, Object[] outObj) {
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
- .resolveActivity(info.getIntent(), info.user);
- outObj[0] = activityInfo;
- return (activityInfo != null) ? appState.getIconCache()
- .getFullResIcon(activityInfo, false) : null;
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- if (info instanceof PendingAddShortcutInfo) {
- ShortcutConfigActivityInfo activityInfo =
- ((PendingAddShortcutInfo) info).activityInfo;
- outObj[0] = activityInfo;
- return activityInfo.getFullResIcon(appState.getIconCache());
- }
- ShortcutKey key = ShortcutKey.fromItemInfo(info);
- DeepShortcutManager sm = DeepShortcutManager.getInstance(mLauncher);
- List<ShortcutInfoCompat> si = sm.queryForFullDetails(
- key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
- if (si.isEmpty()) {
- return null;
- } else {
- outObj[0] = si.get(0);
- return sm.getShortcutIconDrawable(si.get(0),
- appState.getInvariantDeviceProfile().fillResIconDpi);
- }
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
- mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
- if (icon == null) {
- return null;
- }
- outObj[0] = icon;
- return icon;
- } else {
- return null;
- }
- }
-
- /**
* For apps icons and shortcut icons that have badges, this method creates a drawable that can
* later on be rendered on top of the layers for the badges. For app icons, work profile badges
* can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 6fc81c9..7a14b36 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -74,6 +74,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
@@ -84,7 +85,7 @@
/**
* Represents a set of icons chosen by the user or generated by the system.
*/
-public class Folder extends AbstractFloatingView implements DragSource,
+public class Folder extends AbstractFloatingView implements ClipPathView, DragSource,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
@@ -1460,6 +1461,7 @@
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
* rounded rect.
*/
+ @Override
public void setClipPath(Path clipPath) {
mClipPath = clipPath;
invalidate();
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/folder/FolderShape.java
index 61db6ff..ec6078e 100644
--- a/src/com/android/launcher3/folder/FolderShape.java
+++ b/src/com/android/launcher3/folder/FolderShape.java
@@ -39,6 +39,7 @@
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.View;
import android.view.ViewOutlineProvider;
import com.android.launcher3.R;
@@ -46,6 +47,7 @@
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ClipPathView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,8 +76,8 @@
public abstract void addShape(Path path, float offsetX, float offsetY, float radius);
- public abstract Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed);
+ public abstract <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed);
@Nullable
public TypedValue getAttrValue(int attr) {
@@ -88,8 +90,8 @@
private static abstract class SimpleRectShape extends FolderShape {
@Override
- public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed) {
+ public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
return new RoundedRectRevealOutlineProvider(
getStartRadius(startRect), endRadius, startRect, endRect) {
@Override
@@ -121,8 +123,8 @@
Rect startRect, Rect endRect, float endRadius, Path outPath);
@Override
- public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
- float endRadius, boolean isReversed) {
+ public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+ Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
Path path = new Path();
AnimatorUpdateListener listener =
newUpdateListener(startRect, endRect, endRadius, path);
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 6fac31e2..66f9dbf 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -16,9 +16,16 @@
package com.android.launcher3.graphics;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_USER_PRESENT;
+
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -89,6 +96,20 @@
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (ACTION_SCREEN_OFF.equals(action)) {
+ mAnimateScrimOnNextDraw = true;
+ } else if (ACTION_USER_PRESENT.equals(action)) {
+ // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
+ // the user unlocked and the Launcher is not in the foreground.
+ mAnimateScrimOnNextDraw = false;
+ }
+ }
+ };
+
private static final int DARK_SCRIM_COLOR = 0x55000000;
private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
private static final int ALPHA_MASK_HEIGHT_DP = 500;
@@ -204,11 +225,20 @@
public void onViewAttachedToWindow(View view) {
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
+
+ if (mTopScrim != null) {
+ IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
+ filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
+ mRoot.getContext().registerReceiver(mReceiver, filter);
+ }
}
@Override
public void onViewDetachedFromWindow(View view) {
mWallpaperColorInfo.removeOnChangeListener(this);
+ if (mTopScrim != null) {
+ mRoot.getContext().unregisterReceiver(mReceiver);
+ }
}
@Override
diff --git a/src/com/android/launcher3/views/ClipPathView.java b/src/com/android/launcher3/views/ClipPathView.java
new file mode 100644
index 0000000..2152e1d
--- /dev/null
+++ b/src/com/android/launcher3/views/ClipPathView.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.views;
+
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * Alternative to using {@link View#getClipToOutline()} as it only works with derivatives of
+ * rounded rect.
+ */
+public interface ClipPathView {
+ void setClipPath(Path clipPath);
+}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 07318c9..2b9e7b6 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -16,41 +16,86 @@
package com.android.launcher3.views;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.icons.LauncherIcons;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
-public class FloatingIconView extends View implements Animator.AnimatorListener {
+
+public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
private Runnable mStartRunnable;
private Runnable mEndRunnable;
- public FloatingIconView(Context context) {
- super(context);
- }
+ private Drawable mDrawable;
+ private int mOriginalHeight;
+ private final int mBlurSizeOutline;
- public void setRunnables(Runnable startRunnable, Runnable endRunnable) {
- mStartRunnable = startRunnable;
- mEndRunnable = endRunnable;
+ private boolean mIsAdaptiveIcon = false;
+
+ private @Nullable Drawable mForeground;
+ private @Nullable Drawable mBackground;
+ private ValueAnimator mRevealAnimator;
+ private final Rect mStartRevealRect = new Rect();
+ private final Rect mEndRevealRect = new Rect();
+ private Path mClipPath;
+ protected final Rect mOutline = new Rect();
+ private final float mTaskCornerRadius;
+
+ private final Rect mFinalDrawableBounds = new Rect();
+ private final Rect mBgDrawableBounds = new Rect();
+ private final float mBgDrawableStartScale = 5f; // Magic number that can be tuned later.
+
+ private FloatingIconView(Context context) {
+ super(context);
+
+ mBlurSizeOutline = context.getResources().getDimensionPixelSize(
+ R.dimen.blur_size_medium_outline);
+
+ mTaskCornerRadius = 0; // TODO
}
/**
* Positions this view to match the size and location of {@param rect}.
+ *
+ * @param alpha The alpha to set this view.
+ * @param progress A value from [0, 1] that represents the animation progress.
+ * @param windowAlphaThreshold The value at which the window alpha is 0.
*/
- public void update(RectF rect, float alpha) {
+ public void update(RectF rect, float alpha, float progress, float windowAlphaThreshold) {
setAlpha(alpha);
LayoutParams lp = (LayoutParams) getLayoutParams();
@@ -59,13 +104,44 @@
setTranslationX(dX);
setTranslationY(dY);
- float scaleX = rect.width() / (float) getWidth();
- float scaleY = rect.height() / (float) getHeight();
- float scale = Math.min(scaleX, scaleY);
+ float scaleX = rect.width() / (float) lp.width;
+ float scaleY = rect.height() / (float) lp.height;
+ float scale = mIsAdaptiveIcon ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
+
+ // Wait until the window is no longer visible before morphing the icon into its final shape.
+ float shapeRevealProgress = Utilities.mapToRange(Math.max(windowAlphaThreshold, progress),
+ windowAlphaThreshold, 1f, 0f, 1, Interpolators.LINEAR);
+ if (mIsAdaptiveIcon && shapeRevealProgress > 0) {
+ if (mRevealAnimator == null) {
+ mEndRevealRect.set(mOutline);
+ // We play the reveal animation in reverse so that we end with the icon shape.
+ mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
+ mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, true);
+ mRevealAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRevealAnimator = null;
+ }
+ });
+ mRevealAnimator.start();
+ // We pause here so we can set the current fraction ourselves.
+ mRevealAnimator.pause();
+ }
+
+ float bgScale = shapeRevealProgress + mBgDrawableStartScale * (1 - shapeRevealProgress);
+ setBackgroundDrawableBounds(bgScale);
+
+ mRevealAnimator.setCurrentFraction(shapeRevealProgress);
+ if (Float.compare(shapeRevealProgress, 1f) >= 0f) {
+ mRevealAnimator.end();
+ }
+ }
+ invalidate();
+ invalidateOutline();
}
@Override
@@ -82,25 +158,17 @@
}
}
- @Override
- public void onAnimationCancel(Animator animator) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animator) {
- }
-
/**
* Sets the size and position of this view to match {@param v}.
*
* @param v The view to copy
- * @param hideOriginal If true, it will hide {@param v} while this view is visible.
* @param positionOut Rect that will hold the size and position of v.
*/
- public void matchPositionOf(Launcher launcher, View v, boolean hideOriginal, Rect positionOut) {
+ private void matchPositionOf(Launcher launcher, View v, Rect positionOut) {
Utilities.getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
lp.ignoreInsets = true;
+ mOriginalHeight = lp.height;
// Position the floating view exactly on top of the original
lp.leftMargin = positionOut.left;
@@ -110,29 +178,173 @@
// animation frame.
layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ lp.height);
+ }
- if (v instanceof BubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
- // Create a copy of the app icon
- setBackground(DrawableFactory.INSTANCE.get(launcher)
- .newIcon(v.getContext(), (ItemInfoWithIcon) v.getTag()));
+ @WorkerThread
+ private void getIcon(Launcher launcher, ItemInfo info, boolean useDrawableAsIs,
+ float aspectRatio) {
+ final LayoutParams lp = (LayoutParams) getLayoutParams();
+ mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height, new Object[1]);
+
+ if (ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ && mDrawable instanceof AdaptiveIconDrawable) {
+ mIsAdaptiveIcon = true;
+
+ AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) mDrawable;
+ Drawable background = adaptiveIcon.getBackground();
+ if (background == null) {
+ background = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mBackground = background;
+ Drawable foreground = adaptiveIcon.getForeground();
+ if (foreground == null) {
+ foreground = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mForeground = foreground;
+
+ int offset = getOffsetForAdaptiveIconBounds();
+ mFinalDrawableBounds.set(offset, offset, lp.width - offset, mOriginalHeight - offset);
+ mForeground.setBounds(mFinalDrawableBounds);
+ mBackground.setBounds(mFinalDrawableBounds);
+
+ int blurMargin = mBlurSizeOutline / 2;
+ mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
+ mOriginalHeight - blurMargin);
+
+ if (aspectRatio > 0) {
+ lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ + lp.height);
+ setBackgroundDrawableBounds(mBgDrawableStartScale);
+ }
+
+ // Set up outline
+ mOutline.set(0, 0, lp.width, lp.height);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(mOutline, mTaskCornerRadius);
+ }
+ });
+ setClipToOutline(true);
+ } else {
+ setBackground(mDrawable);
+ }
+
+ new Handler(Looper.getMainLooper()).post(() -> {
+ invalidate();
+ invalidateOutline();
+ });
+ }
+
+ private void setBackgroundDrawableBounds(float scale) {
+ mBgDrawableBounds.set(mFinalDrawableBounds);
+ Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale);
+ mBackground.setBounds(mBgDrawableBounds);
+ }
+
+ private int getOffsetForAdaptiveIconBounds() {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
+ !(mDrawable instanceof AdaptiveIconDrawable)) {
+ return 0;
+ }
+
+ final LayoutParams lp = (LayoutParams) getLayoutParams();
+ Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline);
+ bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
+
+ try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) {
+ Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(mDrawable, null));
+ }
+
+ bounds.inset(
+ (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+ (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+ );
+
+ return bounds.left;
+ }
+
+ @Override
+ public void setClipPath(Path clipPath) {
+ mClipPath = clipPath;
+ invalidate();
+ }
+
+ private void drawAdaptiveIconIfExists(Canvas canvas) {
+ if (mBackground != null) {
+ mBackground.draw(canvas);
+ }
+ if (mForeground != null) {
+ mForeground.draw(canvas);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mClipPath == null) {
+ super.draw(canvas);
+ drawAdaptiveIconIfExists(canvas);
+ } else {
+ int count = canvas.save();
+ canvas.clipPath(mClipPath);
+ super.draw(canvas);
+ drawAdaptiveIconIfExists(canvas);
+ canvas.restoreToCount(count);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {}
+
+ /**
+ * Creates a floating icon view for {@param originalView}.
+ *
+ * @param originalView The view to copy
+ * @param hideOriginal If true, it will hide {@param originalView} while this view is visible.
+ * @param useDrawableAsIs If true, we do not separate the foreground/background of adaptive
+ * icons. TODO(b/122843905): We can remove this once app opening uses new animation.
+ * @param aspectRatio If >= 0, we will use this aspect ratio for the initial adaptive icon size.
+ * @param positionOut Rect that will hold the size and position of v.
+ */
+ public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
+ boolean hideOriginal, boolean useDrawableAsIs, float aspectRatio, Rect positionOut,
+ FloatingIconView recycle) {
+ FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher);
+
+ // Match the position of the original view.
+ view.matchPositionOf(launcher, originalView, positionOut);
+
+ // Get the drawable on the background thread
+ // Must be called after matchPositionOf so that we know what size to load.
+ if (originalView.getTag() instanceof ItemInfo) {
+ new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
+ view.getIcon(launcher, (ItemInfo) originalView.getTag(), useDrawableAsIs,
+ aspectRatio);
+ });
}
// We need to add it to the overlay, but keep it invisible until animation starts..
final DragLayer dragLayer = launcher.getDragLayer();
- setVisibility(INVISIBLE);
- ((ViewGroup) dragLayer.getParent()).getOverlay().add(this);
+ view.setVisibility(INVISIBLE);
+ ((ViewGroup) dragLayer.getParent()).getOverlay().add(view);
- setRunnables(() -> {
- setVisibility(VISIBLE);
- if (hideOriginal) {
- v.setVisibility(INVISIBLE);
- }
- },
- () -> {
- ((ViewGroup) dragLayer.getParent()).getOverlay().remove(this);
- if (hideOriginal) {
- v.setVisibility(VISIBLE);
- }
- });
+ view.mStartRunnable = () -> {
+ view.setVisibility(VISIBLE);
+ if (hideOriginal) {
+ originalView.setVisibility(INVISIBLE);
+ }
+ };
+ view.mEndRunnable = () -> {
+ ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);
+ if (hideOriginal) {
+ originalView.setVisibility(VISIBLE);
+ }
+ };
+ return view;
}
}