Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,16 @@ open class ComposeSearchLayout(context: Context, attrs: AttributeSet? = null) :
mAppsView?.onClearSearchResult()
}

fun startSearch() {
override fun startSearch() {
startSearch("")
}

fun startSearch(searchQuery: String) {
query.value = searchQuery.trim()
textFieldFocusRequester.requestFocus()
if (::textFieldFocusRequester.isInitialized) {
textFieldFocusRequester.requestFocus()
keyboardController?.show()
}
}

override fun getEditText(): ExtendedEditText? = null
Expand Down
24 changes: 21 additions & 3 deletions src/com/android/launcher3/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
Expand Down Expand Up @@ -1638,10 +1639,27 @@ private void toggleAllApps(boolean alreadyOnHome, boolean focusSearch) {
new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (focusSearch
&& mAppsView.getSearchUiManager().getEditText() != null) {
mAppsView.getSearchUiManager().getEditText().requestFocus();
if (!focusSearch) {
return;
}
if (hasWindowFocus()) {
mAppsView.getSearchUiManager().startSearch();
return;
}
ViewTreeObserver.OnWindowFocusChangeListener listener =
new ViewTreeObserver.OnWindowFocusChangeListener() {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
mAppsView.getSearchUiManager().startSearch();
getWindow().getDecorView()
.getViewTreeObserver()
.removeOnWindowFocusChangeListener(this);
}
}
};
getWindow().getDecorView().getViewTreeObserver()
.addOnWindowFocusChangeListener(listener);
}
});
}
Expand Down
5 changes: 5 additions & 0 deletions src/com/android/launcher3/allapps/SearchUiManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public interface SearchUiManager {
@Nullable
ExtendedEditText getEditText();

/**
* Requests focus on the search field and shows the keyboard if appropriate.
*/
default void startSearch() {}

/**
* Hint to the edit text that it is about to be focused or unfocused. This can be used to start
* animating the edit box accordingly, e.g. after a gesture completes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,9 @@ public void setInsets(Rect insets) {
public ExtendedEditText getEditText() {
return this;
}

@Override
public void startSearch() {
mSearchBarController.focusSearchField();
}
}
48 changes: 46 additions & 2 deletions src/com/android/launcher3/anim/SpringAnimationBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.FloatProperty;
import android.util.Log;

import androidx.annotation.FloatRange;
import androidx.dynamicanimation.animation.SpringForce;
Expand All @@ -33,6 +34,8 @@
*/
public class SpringAnimationBuilder {

private static final String TAG = "SpringAnimationBuilder";

private final Context mContext;

private float mStartValue;
Expand Down Expand Up @@ -134,6 +137,26 @@ private float getValue(float time) {
}

public SpringAnimationBuilder computeParams() {
if (!Float.isFinite(mStartValue) || !Float.isFinite(mEndValue)) {
throw new IllegalArgumentException("Start/End values must be finite. start="
+ mStartValue + " end=" + mEndValue);
}
if (!Float.isFinite(mStiffness) || mStiffness <= 0) {
throw new IllegalArgumentException("Stiffness must be finite and positive: "
+ mStiffness);
}
if (!Float.isFinite(mDampingRatio) || mDampingRatio <= 0 || mDampingRatio >= 1) {
throw new IllegalArgumentException("Damping ratio must be finite and in (0,1): "
+ mDampingRatio);
}
if (!Float.isFinite(mMinVisibleChange) || mMinVisibleChange <= 0) {
throw new IllegalArgumentException("Minimum visible change must be finite and positive: "
+ mMinVisibleChange);
}
if (!Float.isFinite(mVelocity)) {
throw new IllegalArgumentException("Start velocity must be finite: " + mVelocity);
}

int singleFrameMs = RefreshRateTracker.getSingleFrameMs(mContext);
double naturalFreq = Math.sqrt(mStiffness);
double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
Expand Down Expand Up @@ -194,8 +217,29 @@ public <T> ValueAnimator build(T target, FloatProperty<T> property) {

ValueAnimator animator = ValueAnimator.ofFloat(0, mDuration);
animator.setDuration(getDuration()).setInterpolator(LINEAR);
animator.addUpdateListener(anim ->
property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
animator.addUpdateListener(anim -> {
float fraction = anim.getAnimatedFraction();
if (Float.isNaN(fraction)) {
// Skip updates before the animator has a valid fraction (can happen for
// zero-duration children inside an AnimatorSet when setCurrentPlayTime is called).
return;
}
float value = getInterpolatedValue(fraction);
if (!Float.isFinite(value)) {
Log.e(TAG, "Non-finite spring value for target=" + target.getClass().getName()
+ " property=" + property.getName()
+ " start=" + mStartValue
+ " end=" + mEndValue
+ " stiffness=" + mStiffness
+ " damping=" + mDampingRatio
+ " minVisibleChange=" + mMinVisibleChange
+ " velocity=" + mVelocity
+ " fraction=" + fraction
+ " value=" + value);
value = mEndValue;
}
property.set(target, value);
});
animator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animation) {
Expand Down
13 changes: 8 additions & 5 deletions src/com/android/launcher3/folder/ClipRevealData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,21 @@ data class ClipRevealData(
val pageStart = page * layoutParams.width

// Setup start and end area for revealing Folder Content
val safeInitialFolderScale = if (initialFolderScale > 0f && !initialFolderScale.isNaN()) {
initialFolderScale
} else 1f
val extraRadius =
((deviceProfile.folderIconSizePx / initialFolderScale) *
((deviceProfile.folderIconSizePx / safeInitialFolderScale) *
EXTRA_FOLDER_REVEAL_RADIUS_PERCENTAGE)
.toInt()
val contentStart =
Rect(
(pageStart + (backgroundStartRect.left / initialFolderScale)).toInt() -
(pageStart + (backgroundStartRect.left / safeInitialFolderScale)).toInt() -
extraRadius,
(backgroundStartRect.top / initialFolderScale).toInt() - extraRadius,
(pageStart + (backgroundStartRect.right / initialFolderScale)).toInt() +
(backgroundStartRect.top / safeInitialFolderScale).toInt() - extraRadius,
(pageStart + (backgroundStartRect.right / safeInitialFolderScale)).toInt() +
extraRadius,
(backgroundStartRect.bottom / initialFolderScale).toInt() + extraRadius,
(backgroundStartRect.bottom / safeInitialFolderScale).toInt() + extraRadius,
)
val contentEnd =
Rect(pageStart, 0, pageStart + layoutParams.width, layoutParams.height)
Expand Down
46 changes: 38 additions & 8 deletions src/com/android/launcher3/folder/Folder.java
Original file line number Diff line number Diff line change
Expand Up @@ -827,8 +827,24 @@ private void animateOpen(List<ItemInfo> items, int pageNo) {
Log.d("b/383526431", "animateOpen: content child count after cancelling"
+ " animation: " + mContent.getTotalChildCount());

AnimatorSet animatorSet = getFolderAnimationManager()
.createAnimatorSet(/* isOpening */ true);
AnimatorSet animatorSet;
try {
animatorSet = getFolderAnimationManager()
.createAnimatorSet(/* isOpening */ true);
} catch (Exception e) {
Log.e(TAG, "Failed to create folder open animator, opening without animation", e);
mFolderIcon.setIconVisible(false);
mFolderIcon.drawLeaveBehindIfExists();
setState(STATE_OPEN);
announceAccessibilityChanges();
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
FOLDER_OPENED_MESSAGE);
mContent.setFocusOnFirstChild();
if (mPageIndicator != null) {
mPageIndicator.stopAllAnimations();
}
return;
}

animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
Expand Down Expand Up @@ -896,12 +912,26 @@ public void onAnimationEnd(Animator animation) {
// {@link AnimatorListener} before it so that {@link AnimatorListener#onAnimationStart} can
// be called to register mCurrentAnimator, which will be used to cancel animator
addAnimationStartListeners(animatorSet);
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
Log.d("b/311077782", "Folder.animateOpen");
animatorSet.setCurrentPlayTime(Math.min(
getSingleFrameMs(getContext()), animatorSet.getTotalDuration()));
animatorSet.start();
try {
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
Log.d("b/311077782", "Folder.animateOpen");
animatorSet.setCurrentPlayTime(Math.min(
getSingleFrameMs(getContext()), animatorSet.getTotalDuration()));
animatorSet.start();
} catch (Exception e) {
Log.e(TAG, "Failed to start folder open animation, opening without animation", e);
mFolderIcon.setIconVisible(false);
mFolderIcon.drawLeaveBehindIfExists();
setState(STATE_OPEN);
announceAccessibilityChanges();
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
FOLDER_OPENED_MESSAGE);
mContent.setFocusOnFirstChild();
if (mPageIndicator != null) {
mPageIndicator.stopAllAnimations();
}
}


// Make sure the folder picks up the last drag move even if the finger doesn't move.
Expand Down
37 changes: 31 additions & 6 deletions src/com/android/launcher3/folder/FolderAnimationData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,33 @@ data class FolderAnimationData(
)
val scaledFolderRadius: Int = previewBackground.scaledRadius
val baseIconSize: Float = getBubbleTextView(itemsInPreview[0]).iconSize.toFloat()
val initialFolderSize = (scaledFolderRadius * 2) * scaleRelativeToDragLayer
val initialFolderScale = previewSize / baseIconSize * scaleRelativeToDragLayer
// Guard against unlaid-out icons (can happen when an overlay window triggers
// a premature layout), which would otherwise produce NaN/Inf scale values.
val safeScaleRelativeToDragLayer =
if (scaleRelativeToDragLayer > 0f && !scaleRelativeToDragLayer.isNaN()) {
scaleRelativeToDragLayer
} else 1f
val safeBaseIconSize = when {
baseIconSize > 0f && !baseIconSize.isNaN() -> baseIconSize
previewSize > 0f && !previewSize.isNaN() -> previewSize
else -> 1f
}
val safePreviewSize = when {
previewSize > 0f && !previewSize.isNaN() -> previewSize
safeBaseIconSize > 0f -> safeBaseIconSize
else -> 1f
}
val initialFolderSize = (scaledFolderRadius * 2) * safeScaleRelativeToDragLayer
val initialFolderScale =
(safePreviewSize / safeBaseIconSize * safeScaleRelativeToDragLayer).let {
if (it > 0f && !it.isNaN()) it else 1f
}
android.util.Log.e(
"FolderAnimationData",
"baseIconSize=$baseIconSize previewSize=$previewSize scaleRelativeToDragLayer=$scaleRelativeToDragLayer " +
"safeScale=$safeScaleRelativeToDragLayer safeBase=$safeBaseIconSize safePreview=$safePreviewSize " +
"initialFolderScale=$initialFolderScale"
)

// Get offsets for Previews and Content
val initialPreviewItemOffsetX =
Expand All @@ -90,13 +115,13 @@ data class FolderAnimationData(
val initialX =
((folderIconWorkspacePosition.left +
paddingLeft +
Math.round(previewBackground.offsetX * scaleRelativeToDragLayer)) -
Math.round(previewBackground.offsetX * safeScaleRelativeToDragLayer)) -
contentOffsetX -
initialPreviewItemOffsetX)
val initialY =
((folderIconWorkspacePosition.top +
paddingTop +
Math.round(previewBackground.offsetY * scaleRelativeToDragLayer)) -
Math.round(previewBackground.offsetY * safeScaleRelativeToDragLayer)) -
contentOffsetY)

// Get scaled height of content and radius of background
Expand All @@ -110,7 +135,7 @@ data class FolderAnimationData(
return FolderAnimationData(
isOpening = isOpening,
startScale = if (isOpening) initialFolderScale else 1f,
folderScale = initialFolderScale / scaleRelativeToDragLayer,
folderScale = initialFolderScale / safeScaleRelativeToDragLayer,
xDistance = (initialX - layoutParams.x).toFloat(),
yDistance = (initialY - layoutParams.y).toFloat(),
contentHeightDifference = contentHeightDifference,
Expand All @@ -119,7 +144,7 @@ data class FolderAnimationData(
initialFolderSize = initialFolderSize,
previewOffsetX = initialPreviewItemOffsetX + contentOffsetX,
scaledPreviewOffsetX =
(initialPreviewItemOffsetX / scaleRelativeToDragLayer).toInt() +
(initialPreviewItemOffsetX / safeScaleRelativeToDragLayer).toInt() +
folderRadiusDifference,
contentOffsetY = contentOffsetY,
defaultDuration =
Expand Down
29 changes: 22 additions & 7 deletions src/com/android/launcher3/folder/FolderAnimationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,26 @@ public AnimatorSet createAnimatorSet(boolean isOpening) {
float scaleRelativeToDragLayer = mFolder.mActivityContext.getDragLayer()
.getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
int scaledRadius = mPreviewBackground.getScaledRadius();
float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer;

// Match size/scale of icons in the preview
float previewScale = rule.scaleForItem(itemsInPreview.size(), 0);
float previewSize = rule.getIconSize() * previewScale;
float baseIconSize = getBubbleTextView(itemsInPreview.get(0)).getIconSize();
float initialScale = previewSize / baseIconSize * scaleRelativeToDragLayer;
// Guard against unlaid-out icons (can happen when an overlay window triggers
// a premature layout), which would otherwise produce NaN/Inf scale values.
float safeScaleRelativeToDragLayer = (scaleRelativeToDragLayer > 0f && !Float.isNaN(scaleRelativeToDragLayer))
? scaleRelativeToDragLayer : 1f;
float safeBaseIconSize = (baseIconSize > 0f && !Float.isNaN(baseIconSize))
? baseIconSize : ((previewSize > 0f && !Float.isNaN(previewSize)) ? previewSize : 1f);
float safePreviewSize = (previewSize > 0f && !Float.isNaN(previewSize))
? previewSize : safeBaseIconSize;
float initialScale = safePreviewSize / safeBaseIconSize * safeScaleRelativeToDragLayer;
if (initialScale <= 0f || Float.isNaN(initialScale)) {
initialScale = 1f;
}
float initialSize = (scaledRadius * 2) * safeScaleRelativeToDragLayer;
final float finalScale = 1f;
float scale = mIsOpening ? initialScale : finalScale;
float scale = sanitizeFloat(mIsOpening ? initialScale : finalScale);
mFolder.setPivotX(0);
mFolder.setPivotY(0);

Expand All @@ -180,10 +191,10 @@ public AnimatorSet createAnimatorSet(boolean isOpening) {
final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale);

int initialX = folderIconPos.left + mFolder.getPaddingLeft()
+ Math.round(mPreviewBackground.getOffsetX() * scaleRelativeToDragLayer)
+ Math.round(mPreviewBackground.getOffsetX() * safeScaleRelativeToDragLayer)
- paddingOffsetX - previewItemOffsetX;
int initialY = folderIconPos.top + mFolder.getPaddingTop()
+ Math.round(mPreviewBackground.getOffsetY() * scaleRelativeToDragLayer)
+ Math.round(mPreviewBackground.getOffsetY() * safeScaleRelativeToDragLayer)
- paddingOffsetY;
final float xDistance = initialX - lp.x;
final float yDistance = initialY - lp.y;
Expand Down Expand Up @@ -352,10 +363,10 @@ public void onAnimationEnd(Animator animation) {
}

int radiusDiff = scaledRadius - mPreviewBackground.getRadius();
addPreviewItemAnimators(a, initialScale / scaleRelativeToDragLayer,
addPreviewItemAnimators(a, sanitizeFloat(initialScale / safeScaleRelativeToDragLayer),
// Background can have a scaled radius in drag and drop mode, so we need to add the
// difference to keep the preview items centered.
(int) (previewItemOffsetX / scaleRelativeToDragLayer) + radiusDiff, radiusDiff);
(int) (previewItemOffsetX / safeScaleRelativeToDragLayer) + radiusDiff, radiusDiff);
return a;
}

Expand Down Expand Up @@ -517,4 +528,8 @@ private BubbleTextView getBubbleTextView(View v) {
? ((AppPairIcon) v).getTitleTextView()
: (BubbleTextView) v;
}

private static float sanitizeFloat(float value) {
return (value > 0f && !Float.isNaN(value) && value != Float.POSITIVE_INFINITY && value != Float.NEGATIVE_INFINITY) ? value : 1f;
}
}
Loading