Merge "Play return to icon animation if user swipes back to All Apps." into sc-v2-dev

This commit is contained in:
Jon Miranda
2021-12-06 18:17:52 +00:00
committed by Android (Google) Code Review
6 changed files with 125 additions and 111 deletions

View File

@@ -1214,14 +1214,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
/**
* Returns view on the workspace that corresponds to the closing app in the list of app targets
* Returns view on launcher that corresponds to the closing app in the list of app targets
*/
private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat[] appTargets) {
private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
for (RemoteAnimationTargetCompat appTarget : appTargets) {
if (appTarget.mode == MODE_CLOSING) {
View workspaceView = findWorkspaceView(appTarget);
if (workspaceView != null) {
return workspaceView;
View launcherView = findLauncherView(appTarget);
if (launcherView != null) {
return launcherView;
}
}
}
@@ -1229,9 +1229,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
/**
* Returns view on the workspace that corresponds to the {@param runningTaskTarget}.
* Returns view on launcher that corresponds to the {@param runningTaskTarget}.
*/
private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat runningTaskTarget) {
private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) {
if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
return null;
}
@@ -1269,7 +1269,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
}
return mLauncher.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
return mLauncher.getFirstMatchForAppClose(launchCookieItemId,
packageName, UserHandle.of(runningTaskTarget.taskInfo.userId));
}
@@ -1292,7 +1292,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
* Closing animator that animates the window into its final location on the workspace.
*/
private void getClosingWindowAnimators(AnimatorSet animation,
RemoteAnimationTargetCompat[] targets, View workspaceView) {
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
@@ -1308,17 +1308,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
// Get floating view and target rect.
if (workspaceView instanceof LauncherAppWidgetHostView) {
if (launcherView instanceof LauncherAppWidgetHostView) {
Size windowSize = new Size(mDeviceProfile.availableWidthPx,
mDeviceProfile.availableHeightPx);
int fallbackBackgroundColor =
FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
(LauncherAppWidgetHostView) workspaceView, targetRect, windowSize,
(LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
isTransluscent, fallbackBackgroundColor);
} else if (workspaceView != null) {
floatingIconView = getFloatingIconView(mLauncher, workspaceView,
} else if (launcherView != null) {
floatingIconView = getFloatingIconView(mLauncher, launcherView,
true /* hideOriginal */, targetRect, false /* isOpening */);
} else {
targetRect.set(getDefaultWindowTargetRect());
@@ -1373,15 +1373,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
// Use a fixed velocity to start the animation.
float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
PointF velocity = new PointF(0, -velocityPxPerS);
animation.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, workspaceView).getAnimators());
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
anim.start(mLauncher, velocity);
anim.start(mLauncher, velocityPxPerS);
}
});
}
@@ -1556,22 +1551,30 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (anim == null) {
anim = new AnimatorSet();
View workspaceView = findWorkspaceView(appTargets);
boolean isWorkspaceViewVisible = workspaceView != null
&& !mLauncher.isInState(LauncherState.ALL_APPS)
&& !mLauncher.getWorkspace().isOverlayShown();
boolean playFallBackAnimation = !isWorkspaceViewVisible
&& (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible());
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
View launcherView = findLauncherView(appTargets);
boolean playFallBackAnimation = (launcherView == null
&& launcherIsForceInvisibleOrOpening)
|| mLauncher.getWorkspace().isOverlayShown();
boolean playWorkspaceReveal = true;
if (mFromUnlock) {
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
&& !playFallBackAnimation) {
getClosingWindowAnimators(anim, appTargets, workspaceView);
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
playWorkspaceReveal = false;
// Use a fixed velocity to start the animation.
float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
PointF velocity = new PointF(0, -velocityPxPerS);
getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, launcherView).getAnimators());
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
playWorkspaceReveal = false;
}
} else {
anim.play(getFallbackClosingWindowAnimators(appTargets));
}
@@ -1584,8 +1587,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible()) {
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
// Only register the content animation for cancellation when state changes

View File

@@ -244,7 +244,7 @@ public class LauncherSwipeHandlerV2 extends
}
}
return mActivity.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
return mActivity.getFirstMatchForAppClose(launchCookieItemId,
runningTaskView.getTask().key.getComponent().getPackageName(),
UserHandle.of(runningTaskView.getTask().key.userId));
}

View File

@@ -29,6 +29,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
@@ -39,6 +40,7 @@ import static com.android.launcher3.LauncherState.NO_SCALE;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -55,6 +57,7 @@ import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -90,6 +93,7 @@ import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
@@ -215,6 +219,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -2674,6 +2679,79 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
}
}
/**
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
* @param preferredItemId The id of the preferred item to match to if it exists.
* @param packageName The package name of the app to match.
* @param user The user of the app to match.
*/
public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
final ItemInfoMatcher preferredItem = (info, cn) ->
info != null && info.id == preferredItemId;
final ItemInfoMatcher packageAndUserAndApp = (info, cn) ->
info != null
&& info.itemType == ITEM_TYPE_APPLICATION
&& info.user.equals(user)
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(),
packageName);
if (isInState(LauncherState.ALL_APPS)) {
return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()),
preferredItem, packageAndUserAndApp);
} else {
List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
mWorkspace.forEachVisiblePage(page
-> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
// Order: Preferred item by itself or in folder, then by matching package/user
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
} else {
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
// FolderAdaptiveIcon as the background.
return getFirstMatch(containers, preferredItem, packageAndUserAndApp);
}
}
}
/**
* Finds the first view matching the ordered operators across the given viewgroups in order.
* @param containers List of ViewGroups to scan, in order of preference.
* @param operators List of operators, in order starting from best matching operator.
*/
private static View getFirstMatch(Iterable<ViewGroup> containers,
final ItemInfoMatcher... operators) {
for (ItemInfoMatcher operator : operators) {
for (ViewGroup container : containers) {
View match = mapOverViewGroup(container, operator);
if (match != null) {
return match;
}
}
}
return null;
}
/**
* Returns the first view matching the operator in the given ViewGroups, or null if none.
* Forward iteration matters.
*/
private static View mapOverViewGroup(ViewGroup container, ItemInfoMatcher op) {
final int itemCount = container.getChildCount();
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
View item = container.getChildAt(itemIdx);
if (op.matchesInfo((ItemInfo) item.getTag())) {
return item;
}
}
return null;
}
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
.setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);

View File

@@ -26,7 +26,6 @@ import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
@@ -51,7 +50,6 @@ import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -3146,62 +3144,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return layouts;
}
/**
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
* @param preferredItemId The id of the preferred item to match to if it exists.
* @param packageName The package name of the app to match.
* @param user The user of the app to match.
*/
public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
final ItemOperator preferredItem = (ItemInfo info, View view) ->
info != null && info.id == preferredItemId;
final ItemOperator preferredItemInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
if (preferredItem.evaluate(shortcutInfo, view)) {
return true;
}
}
}
return false;
};
final ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
info != null
&& info.itemType == ITEM_TYPE_APPLICATION
&& info.user.equals(user)
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(),
packageName);
final ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
if (packageAndUserAndApp.evaluate(shortcutInfo, view)) {
return true;
}
}
}
return false;
};
List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
cellLayouts.add(getHotseat());
forEachVisiblePage(page -> cellLayouts.add((CellLayout) page));
// Order: Preferred item, App icons in hotseat/workspace, app in folder in hotseat/workspace
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
return getFirstMatch(cellLayouts, preferredItem, preferredItemInFolder,
packageAndUserAndApp, packageAndUserAndAppInFolder);
} else {
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
// FolderAdaptiveIcon as the background.
return getFirstMatch(cellLayouts, preferredItem, packageAndUserAndApp);
}
}
public View getHomescreenIconByItemId(final int id) {
return getFirstMatch((info, v) -> info != null && info.id == id);
}
@@ -3227,23 +3169,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return value[0];
}
/**
* Finds the first view matching the ordered operators across the given cell layouts by order.
* @param cellLayouts List of CellLayouts to scan, in order of preference.
* @param operators List of operators, in order starting from best matching operator.
*/
View getFirstMatch(Iterable<CellLayout> cellLayouts, final ItemOperator... operators) {
for (ItemOperator operator : operators) {
for (CellLayout cellLayout : cellLayouts) {
View match = mapOverCellLayout(cellLayout, operator);
if (match != null) {
return match;
}
}
}
return null;
}
void clearDropTargets() {
mapOverItems(new ItemOperator() {
@Override

View File

@@ -20,6 +20,7 @@ import android.content.ComponentName;
import android.os.UserHandle;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -85,8 +86,16 @@ public interface ItemInfoMatcher {
}
static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
keys.contains(ShortcutKey.fromItemInfo(info));
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& keys.contains(ShortcutKey.fromItemInfo(info));
}
/**
* Returns a matcher for items within folders.
*/
static ItemInfoMatcher forFolderMatch(ItemInfoMatcher childOperator) {
return (info, cn) -> info instanceof FolderInfo && ((FolderInfo) info).contents.stream()
.anyMatch(childOperator::matchesInfo);
}
/**

View File

@@ -158,7 +158,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
if (mContract == null) {
return;
}
View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1,
View icon = mLauncher.getFirstMatchForAppClose(-1,
mContract.componentName.getPackageName(), mContract.user);
boolean iconChanged = mIcon != icon;
@@ -182,7 +182,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
lp.topMargin = Math.round(mIconPosition.top);
}
}
if (iconChanged && !mIconBounds.isEmpty()) {
if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
// Record the icon display
setCurrentIconVisible(true);
Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());