use of com.android.launcher3.dragndrop.DragLayer in project android_packages_apps_Launcher3 by ArrowOS.
the class DragLayer method animateView.
/**
* This method animates a view at the end of a drag and drop animation.
* @param view The view to be animated. This view is drawn directly into DragLayer, and so
* doesn't need to be a child of DragLayer.
* @param to The final location of the view. Only the left and top parameters are used. This
* location doesn't account for scaling, and so should be centered about the desired
* final location (including scaling).
* @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
* @param finalScaleX The final scale of the view. The view is scaled about its center.
* @param finalScaleY The final scale of the view. The view is scaled about its center.
* @param duration The duration of the animation.
* @param motionInterpolator The interpolator to use for the location of the view.
* @param onCompleteRunnable Optional runnable to run on animation completion.
* @param animationEndStyle Whether or not to fade out the view once the animation completes.
* {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
* @param anchorView If not null, this represents the view which the animated view stays
*/
public void animateView(final DragView view, final Rect to, final float finalAlpha, final float finalScaleX, final float finalScaleY, int duration, final Interpolator motionInterpolator, final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
view.cancelAnimation();
view.requestLayout();
final int[] from = getViewLocationRelativeToSelf(view);
// Calculate the duration of the animation based on the object's distance
final float dist = (float) Math.hypot(to.left - from[0], to.top - from[1]);
final Resources res = getResources();
final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
// If duration < 0, this is a cue to compute the duration based on the distance
if (duration < 0) {
duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
if (dist < maxDist) {
duration *= DEACCEL_1_5.getInterpolation(dist / maxDist);
}
duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
// Fall back to cubic ease out interpolator for the animation if none is specified
TimeInterpolator interpolator = motionInterpolator == null ? DEACCEL_1_5 : motionInterpolator;
// Animate the view
PendingAnimation anim = new PendingAnimation(duration);
anim.add(ofFloat(view, View.SCALE_X, finalScaleX), interpolator, SpringProperty.DEFAULT);
anim.add(ofFloat(view, View.SCALE_Y, finalScaleY), interpolator, SpringProperty.DEFAULT);
anim.setViewAlpha(view, finalAlpha, interpolator);
anim.setFloat(view, VIEW_TRANSLATE_Y, to.top, interpolator);
ObjectAnimator xMotion = ofFloat(view, VIEW_TRANSLATE_X, to.left);
if (anchorView != null) {
final int startScroll = anchorView.getScrollX();
TypeEvaluator<Float> evaluator = (f, s, e) -> mapRange(f, s, e) + (anchorView.getScaleX() * (startScroll - anchorView.getScrollX()));
xMotion.setEvaluator(evaluator);
}
anim.add(xMotion, interpolator, SpringProperty.DEFAULT);
if (onCompleteRunnable != null) {
anim.addListener(forEndCallback(onCompleteRunnable));
}
playDropAnimation(view, anim.buildAnim(), animationEndStyle);
}
use of com.android.launcher3.dragndrop.DragLayer in project android_packages_apps_Launcher3 by ArrowOS.
the class ArrowPopup method orientAboutObject.
/**
* @see #orientAboutObject()
*
* @param allowAlignLeft Set to false if we already tried aligning left and didn't have room.
* @param allowAlignRight Set to false if we already tried aligning right and didn't have room.
* TODO: Can we test this with all permutations of widths/heights and icon locations + RTL?
*/
private void orientAboutObject(boolean allowAlignLeft, boolean allowAlignRight) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int extraVerticalSpace = mArrowHeight + mArrowOffsetVertical + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
// The margins are added after we call this method, so we need to account for them here.
int numVisibleChildren = 0;
for (int i = getChildCount() - 1; i >= 0; --i) {
if (getChildAt(i).getVisibility() == VISIBLE) {
numVisibleChildren++;
}
}
int childMargins = (numVisibleChildren - 1) * mMargin;
int height = getMeasuredHeight() + extraVerticalSpace + childMargins;
int width = getMeasuredWidth() + getPaddingLeft() + getPaddingRight();
getTargetObjectLocation(mTempRect);
InsettableFrameLayout dragLayer = getPopupContainer();
Rect insets = dragLayer.getInsets();
// Align left (right in RTL) if there is room.
int leftAlignedX = mTempRect.left;
int rightAlignedX = mTempRect.right - width;
mIsLeftAligned = !mIsRtl ? allowAlignLeft : !allowAlignRight;
int x = mIsLeftAligned ? leftAlignedX : rightAlignedX;
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = mTempRect.width();
int xOffset = iconWidth / 2 - mArrowOffsetHorizontal - mArrowWidth / 2;
x += mIsLeftAligned ? xOffset : -xOffset;
// Check whether we can still align as we originally wanted, now that we've calculated x.
if (!allowAlignLeft && !allowAlignRight) {
// We've already tried both ways and couldn't make it fit. onLayout() will set the
// gravity to CENTER_HORIZONTAL, but continue below to update y.
} else {
boolean canBeLeftAligned = x + width + insets.left < dragLayer.getWidth() - insets.right;
boolean canBeRightAligned = x > insets.left;
boolean alignmentStillValid = mIsLeftAligned && canBeLeftAligned || !mIsLeftAligned && canBeRightAligned;
if (!alignmentStillValid) {
// Try again, but don't allow this alignment we already know won't work.
orientAboutObject(allowAlignLeft && !mIsLeftAligned, /* allowAlignLeft */
allowAlignRight && mIsLeftAligned);
return;
}
}
// Open above icon if there is room.
int iconHeight = mTempRect.height();
int y = mTempRect.top - height;
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
if (!mIsAboveIcon) {
y = mTempRect.top + iconHeight + extraVerticalSpace;
}
// Insets are added later, so subtract them now.
x -= insets.left;
y -= insets.top;
mGravity = 0;
if (y + height > dragLayer.getBottom() - insets.bottom) {
// The container is opening off the screen, so just center it in the drag layer instead.
mGravity = Gravity.CENTER_VERTICAL;
// Put the container next to the icon, preferring the right side in ltr (left in rtl).
int rightSide = leftAlignedX + iconWidth - insets.left;
int leftSide = rightAlignedX - iconWidth - insets.left;
if (!mIsRtl) {
if (rightSide + width < dragLayer.getRight()) {
x = rightSide;
mIsLeftAligned = true;
} else {
x = leftSide;
mIsLeftAligned = false;
}
} else {
if (leftSide > dragLayer.getLeft()) {
x = leftSide;
mIsLeftAligned = false;
} else {
x = rightSide;
mIsLeftAligned = true;
}
}
mIsAboveIcon = true;
}
setX(x);
if (Gravity.isVertical(mGravity)) {
return;
}
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams();
if (mIsAboveIcon) {
arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrowOffsetVertical - insets.bottom;
} else {
arrowLp.gravity = lp.gravity = Gravity.TOP;
lp.topMargin = y + insets.top;
arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffsetVertical;
}
}
use of com.android.launcher3.dragndrop.DragLayer in project android_packages_apps_Launcher3 by ArrowOS.
the class ArrowPopup method initColorExtractionLocations.
private void initColorExtractionLocations(Launcher launcher) {
if (mColorExtractors == null) {
return;
}
Workspace workspace = launcher.getWorkspace();
if (workspace == null) {
return;
}
boolean firstVisibleChild = true;
int screenId = workspace.getScreenIdForPageIndex(workspace.getCurrentPage());
DragLayer dragLayer = launcher.getDragLayer();
final View[] viewAlignedWithArrow = new View[1];
// Order matters here, since we need the arrow to match the color of its adjacent view.
for (final View view : getChildrenForColorExtraction()) {
if (view != null && view.getVisibility() == VISIBLE) {
Rect pos = new Rect();
dragLayer.getDescendantRectRelativeToSelf(view, pos);
if (!pos.isEmpty()) {
LocalColorExtractor extractor = LocalColorExtractor.newInstance(launcher);
extractor.setWorkspaceLocation(pos, dragLayer, screenId);
extractor.setListener(extractedColors -> {
AnimatorSet colors = new AnimatorSet();
int newColor = getExtractedColor(extractedColors);
setChildColor(view, newColor, colors);
int numChildren = view instanceof ViewGroup ? ((ViewGroup) view).getChildCount() : 0;
for (int i = 0; i < numChildren; ++i) {
View childView = ((ViewGroup) view).getChildAt(i);
setChildColor(childView, newColor, colors);
}
if (viewAlignedWithArrow[0] == view) {
mArrowColor = newColor;
updateArrowColor();
}
colors.setDuration(150);
view.post(colors::start);
});
mColorExtractors.add(extractor);
if (mIsAboveIcon || firstVisibleChild) {
viewAlignedWithArrow[0] = view;
}
firstVisibleChild = false;
}
}
}
}
use of com.android.launcher3.dragndrop.DragLayer in project android_packages_apps_Launcher3 by ArrowOS.
the class Folder method animateOpen.
/**
* Opens the user folder described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
* is played.
*/
private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
Folder openFolder = getOpen(mActivityContext);
if (openFolder != null && openFolder != this) {
// Close any open folder before opening a folder.
openFolder.close(true);
}
mContent.bindItems(items);
centerAboutIcon();
mItemsInvalidated = true;
updateTextViewFocus();
mIsOpen = true;
BaseDragLayer dragLayer = mActivityContext.getDragLayer();
// There was a one-off crash where the folder had a parent already.
if (getParent() == null) {
dragLayer.addView(this);
mDragController.addDropTarget(this);
} else {
if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:" + getParent());
}
}
mContent.completePendingPageChanges();
mContent.setCurrentPage(pageNo);
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
// leads to an inconsistent state if you drag out of the folder and drag back in without
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted = false;
cancelRunningAnimations();
FolderAnimationManager fam = new FolderAnimationManager(this, true);
AnimatorSet anim = fam.getAnimator();
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mFolderIcon.setIconVisible(false);
mFolderIcon.drawLeaveBehindIfExists();
}
@Override
public void onAnimationEnd(Animator animation) {
mState = STATE_OPEN;
announceAccessibilityChanges();
AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext());
mContent.setFocusOnFirstChild();
}
});
// Footer animation
if (mContent.getPageCount() > 1 && !mInfo.hasOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION)) {
int footerWidth = mContent.getDesiredWidth() - mFooter.getPaddingLeft() - mFooter.getPaddingRight();
float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString());
float translation = (footerWidth - textWidth) / 2;
mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation);
mPageIndicator.prepareEntryAnimation();
// Do not update the flag if we are in drag mode. The flag will be updated, when we
// actually drop the icon.
final boolean updateAnimationFlag = !mDragInProgress;
anim.addListener(new AnimatorListenerAdapter() {
@SuppressLint("InlinedApi")
@Override
public void onAnimationEnd(Animator animation) {
mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION).translationX(0).setInterpolator(AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in));
mPageIndicator.playEntryAnimation();
if (updateAnimationFlag) {
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncherDelegate.getModelWriter());
}
}
});
} else {
mFolderName.setTranslationX(0);
}
mPageIndicator.stopAllAnimations();
startAnimation(anim);
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
mDragController.forceTouchMove();
}
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
use of com.android.launcher3.dragndrop.DragLayer in project android_packages_apps_Launcher3 by ArrowOS.
the class FolderIcon method onDrop.
private void onDrop(final WorkspaceItemInfo item, DragObject d, Rect finalRect, float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
item.cellX = -1;
item.cellY = -1;
DragView animateView = d.dragView;
// will not have a view to animate
if (animateView != null && mActivity instanceof Launcher) {
final Launcher launcher = (Launcher) mActivity;
DragLayer dragLayer = launcher.getDragLayer();
Rect to = finalRect;
if (to == null) {
to = new Rect();
Workspace workspace = launcher.getWorkspace();
// Set cellLayout and this to it's final state to compute final animation locations
workspace.setFinalTransitionTransform();
float scaleX = getScaleX();
float scaleY = getScaleY();
setScaleX(1.0f);
setScaleY(1.0f);
scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
// Finished computing final animation locations, restore current state
setScaleX(scaleX);
setScaleY(scaleY);
workspace.resetTransitionTransform();
}
int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
boolean itemAdded = false;
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
mInfo.add(item, index, false);
mCurrentPreviewItems.clear();
mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
int newIndex = mCurrentPreviewItems.indexOf(item);
if (newIndex >= 0) {
// If the item dropped is going to be in the preview, we update the
// index here to reflect its position in the preview.
index = newIndex;
}
mPreviewItemManager.hidePreviewItem(index, true);
mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item);
itemAdded = true;
} else {
removeItem(item, false);
}
}
if (!itemAdded) {
mInfo.add(item, index, true);
}
int[] center = new int[2];
float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
center[0] = Math.round(scaleRelativeToDragLayer * center[0]);
center[1] = Math.round(scaleRelativeToDragLayer * center[1]);
to.offset(center[0] - animateView.getMeasuredWidth() / 2, center[1] - animateView.getMeasuredHeight() / 2);
float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 1f : 0f;
float finalScale = scale * scaleRelativeToDragLayer;
// Account for potentially different icon sizes with non-default grid settings
if (d.dragSource instanceof AllAppsContainerView) {
DeviceProfile grid = mActivity.getDeviceProfile();
float containerScale = (1f * grid.iconSizePx / grid.allAppsIconSizePx);
finalScale *= containerScale;
}
final int finalIndex = index;
dragLayer.animateView(animateView, to, finalAlpha, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, Interpolators.DEACCEL_2, () -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
}, DragLayer.ANIMATION_END_DISAPPEAR, null);
mFolder.hideItem(item);
if (!itemAdded)
mPreviewItemManager.hidePreviewItem(index, true);
FolderNameInfos nameInfos = new FolderNameInfos();
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.MODEL_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(getContext(), mInfo.contents, nameInfos);
showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
});
} else {
showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
}
} else {
addItem(item);
}
}
Aggregations