Merge "App to home animation zooms into the app icon." into ub-launcher3-master

This commit is contained in:
TreeHugger Robot 2019-02-15 02:05:27 +00:00 committed by Android (Google) Code Review
commit d202bf40bf
8 changed files with 419 additions and 86 deletions

View File

@ -29,6 +29,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
@ -51,6 +52,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.LauncherLayoutListener;
@ -108,17 +110,41 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
@NonNull
@Override
public HomeAnimationFactory prepareHomeUI(Launcher activity) {
DeviceProfile dp = activity.getDeviceProfile();
final DeviceProfile dp = activity.getDeviceProfile();
final RecentsView recentsView = activity.getOverviewPanel();
final ComponentName component = recentsView.getRunningTaskView().getTask().key
.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);
}
return new HomeAnimationFactory() {
@Nullable
@Override
public View getFloatingView() {
return floatingView;
}
@NonNull
@Override
public RectF getWindowTargetRect() {
int halfIconSize = dp.iconSizePx / 2;
float targetCenterX = dp.availableWidthPx / 2;
float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
final int halfIconSize = dp.iconSizePx / 2;
final float targetCenterX = dp.availableWidthPx / 2f;
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
if (workspaceView != null) {
return new RectF(iconLocation);
} else {
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
}
}
@NonNull

View File

@ -59,10 +59,10 @@ import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
@ -134,7 +134,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
private final float mClosingWindowTransY;
private DeviceProfile mDeviceProfile;
private View mFloatingView;
private FloatingIconView mFloatingView;
private RemoteAnimationProvider mRemoteAnimationProvider;
@ -404,7 +404,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
if (mFloatingView == null) {
mFloatingView = new View(mLauncher);
mFloatingView = new FloatingIconView(mLauncher);
} else {
mFloatingView.setTranslationX(0);
mFloatingView.setTranslationY(0);
@ -413,58 +413,15 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
mFloatingView.setAlpha(1);
mFloatingView.setBackground(null);
}
if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
// Create a copy of the app icon
mFloatingView.setBackground(DrawableFactory.INSTANCE.get(mLauncher)
.newIcon(v.getContext(), (ItemInfoWithIcon) v.getTag()));
}
// Position the floating view exactly on top of the original
Rect rect = new Rect();
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
if (fromDeepShortcutView) {
// Deep shortcut views have their icon drawn in a separate view.
DeepShortcutView view = (DeepShortcutView) v.getParent();
mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
} else {
mDragLayer.getDescendantRectRelativeToSelf(v, rect);
}
int viewLocationLeft = rect.left;
int viewLocationTop = rect.top;
mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
float startScale = 1f;
if (isBubbleTextView && !fromDeepShortcutView) {
BubbleTextView btv = (BubbleTextView) v;
btv.getIconBounds(rect);
Drawable dr = btv.getIcon();
if (dr instanceof FastBitmapDrawable) {
startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
}
} else {
rect.set(0, 0, rect.width(), rect.height());
}
viewLocationLeft += rect.left;
viewLocationTop += rect.top;
int viewLocationStart = mIsRtl
? windowTargetBounds.width() - rect.right
: viewLocationLeft;
LayoutParams lp = new LayoutParams(rect.width(), rect.height());
lp.ignoreInsets = true;
lp.leftMargin = viewLocationStart;
lp.topMargin = viewLocationTop;
int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
// Special RTL logic is needed to handle the window target bounds.
lp.leftMargin = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
mFloatingView.setLayoutParams(lp);
// Set the properties here already to make sure they'are available when running the first
// animation frame.
mFloatingView.layout(viewLocationLeft, viewLocationTop,
viewLocationLeft + rect.width(), viewLocationTop + rect.height());
// Swap the two views in place.
((ViewGroup) mDragLayer.getParent()).getOverlay().add(mFloatingView);
if (toggleVisibility) {
v.setVisibility(View.INVISIBLE);
}
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
@ -475,8 +432,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
float xPosition = mIsRtl
? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
: lp.getMarginStart();
float dX = centerX - xPosition - (lp.width / 2);
float dY = centerY - lp.topMargin - (lp.height / 2);
float dX = centerX - xPosition - (lp.width / 2f);
float dY = centerY - lp.topMargin - (lp.height / 2f);
ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
@ -502,6 +459,14 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
float maxScaleX = windowTargetBounds.width() / (float) rect.width();
float maxScaleY = windowTargetBounds.height() / (float) rect.height();
float scale = Math.max(maxScaleX, maxScaleY);
float startScale = 1f;
if (isBubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
Drawable dr = ((BubbleTextView) v).getIcon();
if (dr instanceof FastBitmapDrawable) {
startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
}
}
ObjectAnimator scaleAnim = ObjectAnimator
.ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
scaleAnim.setDuration(APP_LAUNCH_DURATION)
@ -521,6 +486,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
alpha.setInterpolator(LINEAR);
appOpenAnimator.play(alpha);
appOpenAnimator.addListener(mFloatingView);
appOpenAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {

View File

@ -151,6 +151,11 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
interface HomeAnimationFactory {
/** Return the floating view that will animate in sync with the closing window. */
default @Nullable View getFloatingView() {
return null;
}
@NonNull RectF getWindowTargetRect();
@NonNull Animator createActivityAnimationToHome();

View File

@ -82,9 +82,11 @@ import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.ActivityControlHelper.LayoutListener;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.RemoteAnimationTargetSet;
@ -924,13 +926,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
mGestureEndTarget = target;
ActivityControlHelper.HomeAnimationFactory homeAnimFactory;
HomeAnimationFactory homeAnimFactory;
Animator windowAnim;
if (mGestureEndTarget == HOME) {
if (mActivity != null) {
homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
} else {
homeAnimFactory = new ActivityControlHelper.HomeAnimationFactory() {
homeAnimFactory = new HomeAnimationFactory() {
@NonNull
@Override
public RectF getWindowTargetRect() {
@ -948,7 +950,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
}
windowAnim = createWindowAnimationToHome(start, homeAnimFactory.getWindowTargetRect());
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
mLauncherTransitionController = null;
} else {
windowAnim = mCurrentShift.animateToValue(start, end);
@ -998,20 +1000,25 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
/**
* Creates an Animator that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param endTarget Where to animate the window towards.
* @param homeAnimationFactory The home animation factory.
*/
private Animator createWindowAnimationToHome(float startProgress, RectF endTarget) {
private Animator createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
mTransformParams.setProgress(startProgress)));
RectF originalTarget = new RectF(mClipAnimationHelper.getTargetRect());
final RectF finalTarget = endTarget;
final RectF finalTarget = homeAnimationFactory.getWindowTargetRect();
final RectFEvaluator rectFEvaluator = new RectFEvaluator();
final RectF targetRect = new RectF();
final RectF currentRect = new RectF();
final View floatingView = homeAnimationFactory.getFloatingView();
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
if (floatingView instanceof FloatingIconView) {
anim.addListener((FloatingIconView) floatingView);
}
anim.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
@ -1026,6 +1033,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
.setSyncTransactionApplier(mSyncTransactionApplier);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
if (floatingView instanceof FloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
}
});
anim.addListener(new AnimationSuccessListener() {
@Override

View File

@ -26,12 +26,15 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
public class DeviceProfile {
public final InvariantDeviceProfile inv;
@ -574,6 +577,33 @@ public class DeviceProfile {
}
}
/**
* Gets an item's location on the home screen. This is useful if the home screen
* is animating, otherwise use {@link View#getLocationOnScreen(int[])}.
*
* TODO(b/123900446): Handle landscape mode
* @param pageDiff The page difference relative to the current page.
*/
public void getItemLocation(int cellX, int cellY, int spanX, int spanY, int container,
int pageDiff, Rect outBounds) {
outBounds.setEmpty();
outBounds.left = mInsets.left
+ workspacePadding.left + cellLayoutPaddingLeftRightPx + (cellX * getCellSize().x);
outBounds.top = mInsets.top;
if (container == CONTAINER_HOTSEAT) {
outBounds.top += workspacePadding.top
+ (inv.numRows * getCellSize().y)
+ verticalDragHandleSizePx
- verticalDragHandleOverlapWorkspace;
outBounds.bottom = outBounds.top + hotseatBarSizePx - hotseatBarBottomPaddingPx;
} else {
outBounds.top += workspacePadding.top + (cellY * getCellSize().y);
outBounds.bottom = outBounds.top + (getCellSize().y * spanY);
outBounds.left += (pageDiff) * availableWidthPx;
}
outBounds.right = outBounds.left + (getCellSize().x * spanX);
}
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;

View File

@ -52,6 +52,8 @@ import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.IntArray;
import java.io.Closeable;
@ -65,6 +67,9 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
/**
* Various utilities shared amongst the Launcher's classes.
*/
@ -541,4 +546,48 @@ public final class Utilities {
public static String getPointString(int x, int y) {
return String.format(Locale.ENGLISH, "%d,%d", x, y);
}
/**
* Returns the location bounds of a view.
* - For DeepShortcutView, we return the bounds of the icon view.
* - For BubbleTextView, we return the icon bounds.
*/
public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
final DragLayer dragLayer = launcher.getDragLayer();
final boolean isBubbleTextView = v instanceof BubbleTextView;
final Rect rect = new Rect();
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
if (fromDeepShortcutView) {
// 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
&& (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
|| ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
BubbleTextView btv = (BubbleTextView) v;
CellLayout pageViewIsOn = ((CellLayout) btv.getParent().getParent());
int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
DeviceProfile dp = launcher.getDeviceProfile();
ItemInfo info = ((ItemInfo) btv.getTag());
dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
} else {
dragLayer.getDescendantRectRelativeToSelf(v, rect);
}
int viewLocationLeft = rect.left;
int viewLocationTop = rect.top;
if (isBubbleTextView && !fromDeepShortcutView) {
BubbleTextView btv = (BubbleTextView) v;
btv.getIconBounds(rect);
} else {
rect.set(0, 0, rect.width(), rect.height());
}
viewLocationLeft += rect.left;
viewLocationTop += rect.top;
outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + rect.width(),
viewLocationTop + rect.height());
}
}

View File

@ -34,6 +34,7 @@ import android.annotation.SuppressLint;
import android.app.WallpaperManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@ -97,6 +98,7 @@ import com.android.launcher3.widget.PendingAppWidgetHostView;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@ -2889,6 +2891,88 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return layouts;
}
/**
* Returns a list of all the CellLayouts on the Homescreen, starting with
* {@param startPage}, then going outward alternating between pages prior to the startPage,
* and then the pages after the startPage.
* ie. if there are 5 pages [0, 1, 2, 3, 4] and startPage is 1, we return [1, 0, 2, 3, 4].
*/
private CellLayout[] getWorkspaceCellLayouts(int startPage) {
int screenCount = getChildCount();
final CellLayout[] layouts = new CellLayout[screenCount];
int screen = 0;
layouts[screen] = (CellLayout) getChildAt(startPage);
screen++;
for (int i = 1; screen < screenCount; ++i) {
CellLayout prevPage = (CellLayout) getChildAt(startPage - i);
CellLayout nextPage = (CellLayout) getChildAt(startPage + i);
if (prevPage != null) {
layouts[screen] = prevPage;
screen++;
}
if (nextPage != null) {
layouts[screen] = nextPage;
screen++;
}
}
return layouts;
}
/**
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
* @param component The component of the task being dismissed.
*/
public View getFirstMatchForAppClose(ComponentName component) {
final int curPage = getCurrentPage();
final CellLayout currentPage = (CellLayout) getPageAt(curPage);
final Workspace.ItemOperator isItemComponent = (info, view) ->
info != null && Objects.equals(info.getTargetComponent(), component);
final Workspace.ItemOperator isItemInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (ShortcutInfo shortcutInfo : folderInfo.contents) {
if (Objects.equals(shortcutInfo.getTargetComponent(), component)) {
return true;
}
}
}
return false;
};
CellLayout[] hotseatAndCurrentPage = new CellLayout[] { getHotseat(), currentPage };
// First we look if the app itself is in the hotseat or on the current workspace page.
View icon = getFirstMatch(hotseatAndCurrentPage, isItemComponent);
if (icon != null) {
return icon;
}
// Then we look if the app is in a folder on the hotseat or current workspace page.
icon = getFirstMatch(hotseatAndCurrentPage, isItemInFolder);
if (icon != null) {
return icon;
}
// Continue searching for the app or for a folder with the app on other pages of the
// workspace. We skip the current page, since we already searched above.
CellLayout[] allPages = getWorkspaceCellLayouts(curPage);
CellLayout[] page = new CellLayout[1];
for (int i = 1; i < allPages.length; ++i) {
page[0] = allPages[i];
icon = getFirstMatch(page, isItemComponent);
if (icon != null) {
return icon;
}
icon = getFirstMatch(page, isItemInFolder);
if (icon != null) {
return icon;
}
}
return null;
}
public View getHomescreenIconByItemId(final int id) {
return getFirstMatch((info, v) -> info != null && info.id == id);
}
@ -2914,6 +2998,23 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return value[0];
}
private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator operator) {
final View[] value = new View[1];
for (CellLayout cellLayout : cellLayouts) {
mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
if (operator.evaluate(info, v)) {
value[0] = v;
return true;
}
return false;
});
if (value[0] != null) {
break;
}
}
return value[0];
}
void clearDropTargets() {
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
@Override
@ -2992,31 +3093,38 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
*/
public void mapOverItems(boolean recurse, ItemOperator op) {
for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
// map over all the shortcuts on the workspace
final int itemCount = container.getChildCount();
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
View item = container.getChildAt(itemIdx);
ItemInfo info = (ItemInfo) item.getTag();
if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
FolderIcon folder = (FolderIcon) item;
ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
// map over all the children in the folder
final int childCount = folderChildren.size();
for (int childIdx = 0; childIdx < childCount; childIdx++) {
View child = folderChildren.get(childIdx);
info = (ItemInfo) child.getTag();
if (op.evaluate(info, child)) {
return;
}
}
} else {
if (op.evaluate(info, item)) {
return;
if (mapOverCellLayout(recurse, layout, op)) {
return;
}
}
}
private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
// map over all the shortcuts on the workspace
final int itemCount = container.getChildCount();
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
View item = container.getChildAt(itemIdx);
ItemInfo info = (ItemInfo) item.getTag();
if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
FolderIcon folder = (FolderIcon) item;
ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
// map over all the children in the folder
final int childCount = folderChildren.size();
for (int childIdx = 0; childIdx < childCount; childIdx++) {
View child = folderChildren.get(childIdx);
info = (ItemInfo) child.getTag();
if (op.evaluate(info, child)) {
return true;
}
}
} else {
if (op.evaluate(info, item)) {
return true;
}
}
}
return false;
}
void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {

View File

@ -0,0 +1,138 @@
/*
* 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.animation.Animator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
/**
* 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 {
private Runnable mStartRunnable;
private Runnable mEndRunnable;
public FloatingIconView(Context context) {
super(context);
}
public void setRunnables(Runnable startRunnable, Runnable endRunnable) {
mStartRunnable = startRunnable;
mEndRunnable = endRunnable;
}
/**
* Positions this view to match the size and location of {@param rect}.
*/
public void update(RectF rect, float alpha) {
setAlpha(alpha);
LayoutParams lp = (LayoutParams) getLayoutParams();
float dX = rect.left - lp.leftMargin;
float dY = rect.top - lp.topMargin;
setTranslationX(dX);
setTranslationY(dY);
float scaleX = rect.width() / (float) getWidth();
float scaleY = rect.height() / (float) getHeight();
float scale = Math.min(scaleX, scaleY);
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
}
@Override
public void onAnimationStart(Animator animator) {
if (mStartRunnable != null) {
mStartRunnable.run();
}
}
@Override
public void onAnimationEnd(Animator animator) {
if (mEndRunnable != null) {
mEndRunnable.run();
}
}
@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) {
Utilities.getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
lp.leftMargin = positionOut.left;
lp.topMargin = positionOut.top;
setLayoutParams(lp);
// Set the properties here already to make sure they are available when running the first
// 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()));
}
// 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);
setRunnables(() -> {
setVisibility(VISIBLE);
if (hideOriginal) {
v.setVisibility(INVISIBLE);
}
},
() -> {
((ViewGroup) dragLayer.getParent()).getOverlay().remove(this);
if (hideOriginal) {
v.setVisibility(VISIBLE);
}
});
}
}