Snap for 7439149 from eb1617521c to sc-release
Change-Id: I82b9211dd2d15ecb4497a22c7987e403634c2d40
This commit is contained in:
@@ -37,6 +37,7 @@ import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.IBinder;
|
||||
import android.view.View;
|
||||
import android.window.SplashScreen;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -435,6 +436,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
||||
ActivityOptionsCompat.setLauncherSourceInfo(
|
||||
activityOptions.options, mLastTouchUpTime);
|
||||
}
|
||||
activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
|
||||
addLaunchCookie(item, activityOptions.options);
|
||||
return activityOptions;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.View;
|
||||
import android.window.SplashScreen;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -222,9 +223,11 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
||||
wrapper, RECENTS_LAUNCH_DURATION,
|
||||
RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
|
||||
- STATUS_BAR_TRANSITION_PRE_DELAY);
|
||||
return new ActivityOptionsWrapper(
|
||||
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
|
||||
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
|
||||
onEndCallback);
|
||||
activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
|
||||
return activityOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.window.SplashScreen;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -165,6 +166,9 @@ public interface TaskShortcutFactory {
|
||||
dismissTaskMenuView(mTarget);
|
||||
|
||||
ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
|
||||
if (options != null) {
|
||||
options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
|
||||
}
|
||||
if (options != null
|
||||
&& ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
|
||||
options)) {
|
||||
|
||||
@@ -1358,7 +1358,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mOrientationHandler);
|
||||
int taskWidth = mTempRect.width();
|
||||
int taskHeight = mTempRect.height();
|
||||
if (mRunningTaskId != -1) {
|
||||
if (mFocusedTaskId != -1) {
|
||||
int boxLength = Math.max(taskWidth, taskHeight);
|
||||
if (mFocusedTaskRatio > 1) {
|
||||
taskWidth = boxLength;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/round_rect_folder"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<com.android.launcher3.folder.FolderPagedView
|
||||
|
||||
+74
@@ -40,6 +40,8 @@ import org.robolectric.RuntimeEnvironment;
|
||||
public final class LauncherAppWidgetProviderInfoTest {
|
||||
|
||||
private static final int CELL_SIZE = 50;
|
||||
private static final int NUM_OF_COLS = 4;
|
||||
private static final int NUM_OF_ROWS = 5;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@@ -75,6 +77,33 @@ public final class LauncherAppWidgetProviderInfoTest {
|
||||
assertThat(info.spanY).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
initSpans_minWidthLargerThanGridColumns_shouldInitializeSpansToAtMostTheGridColumns() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
info.minWidth = CELL_SIZE * (NUM_OF_COLS + 1);
|
||||
info.minHeight = 20;
|
||||
InvariantDeviceProfile idp = createIDP();
|
||||
|
||||
info.initSpans(mContext, idp);
|
||||
|
||||
assertThat(info.spanX).isEqualTo(NUM_OF_COLS);
|
||||
assertThat(info.spanY).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initSpans_minHeightLargerThanGridRows_shouldInitializeSpansToAtMostTheGridRows() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
info.minWidth = 20;
|
||||
info.minHeight = 50 * (NUM_OF_ROWS + 1);
|
||||
InvariantDeviceProfile idp = createIDP();
|
||||
|
||||
info.initSpans(mContext, idp);
|
||||
|
||||
assertThat(info.spanX).isEqualTo(1);
|
||||
assertThat(info.spanY).isEqualTo(NUM_OF_ROWS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initSpans_minResizeWidthUnspecified_shouldInitializeMinSpansToOne() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
@@ -153,6 +182,49 @@ public final class LauncherAppWidgetProviderInfoTest {
|
||||
assertThat(info.minSpanY).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMinSizeFulfilled_minWidthAndHeightWithinGridSize_shouldReturnTrue() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
info.minWidth = 80;
|
||||
info.minHeight = 80;
|
||||
info.minResizeWidth = 50;
|
||||
info.minResizeHeight = 50;
|
||||
InvariantDeviceProfile idp = createIDP();
|
||||
|
||||
info.initSpans(mContext, idp);
|
||||
|
||||
assertThat(info.isMinSizeFulfilled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
isMinSizeFulfilled_minWidthAndMinResizeWidthExceededGridColumns_shouldReturnFalse() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
info.minWidth = CELL_SIZE * (NUM_OF_COLS + 2);
|
||||
info.minHeight = 80;
|
||||
info.minResizeWidth = CELL_SIZE * (NUM_OF_COLS + 1);
|
||||
info.minResizeHeight = 50;
|
||||
InvariantDeviceProfile idp = createIDP();
|
||||
|
||||
info.initSpans(mContext, idp);
|
||||
|
||||
assertThat(info.isMinSizeFulfilled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMinSizeFulfilled_minHeightAndMinResizeHeightExceededGridRows_shouldReturnFalse() {
|
||||
LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
|
||||
info.minWidth = 80;
|
||||
info.minHeight = CELL_SIZE * (NUM_OF_ROWS + 2);
|
||||
info.minResizeWidth = 50;
|
||||
info.minResizeHeight = CELL_SIZE * (NUM_OF_ROWS + 1);
|
||||
InvariantDeviceProfile idp = createIDP();
|
||||
|
||||
info.initSpans(mContext, idp);
|
||||
|
||||
assertThat(info.isMinSizeFulfilled()).isFalse();
|
||||
}
|
||||
|
||||
private InvariantDeviceProfile createIDP() {
|
||||
DeviceProfile profile = Mockito.mock(DeviceProfile.class);
|
||||
doAnswer(i -> {
|
||||
@@ -163,6 +235,8 @@ public final class LauncherAppWidgetProviderInfoTest {
|
||||
|
||||
InvariantDeviceProfile idp = new InvariantDeviceProfile();
|
||||
idp.supportedProfiles.add(profile);
|
||||
idp.numColumns = NUM_OF_COLS;
|
||||
idp.numRows = NUM_OF_ROWS;
|
||||
return idp;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3.anim;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.view.View;
|
||||
@@ -25,7 +26,7 @@ import android.view.ViewGroup;
|
||||
/**
|
||||
* A convenience class to update a view's visibility state after an alpha animation.
|
||||
*/
|
||||
public class AlphaUpdateListener extends AnimationSuccessListener
|
||||
public class AlphaUpdateListener extends AnimatorListenerAdapter
|
||||
implements AnimatorUpdateListener {
|
||||
public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
|
||||
|
||||
@@ -41,7 +42,7 @@ public class AlphaUpdateListener extends AnimationSuccessListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
updateVisibility(mView);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ public class ClippedFolderIconLayoutRule {
|
||||
public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
|
||||
private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
|
||||
|
||||
private static final float MIN_SCALE = 0.48f;
|
||||
private static final float MAX_SCALE = 0.58f;
|
||||
private static final float MAX_RADIUS_DILATION = 0.15f;
|
||||
private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
|
||||
private static final float MIN_SCALE = 0.44f;
|
||||
private static final float MAX_SCALE = 0.54f;
|
||||
private static final float MAX_RADIUS_DILATION = 0.10f;
|
||||
private static final float ITEM_RADIUS_SCALE_FACTOR = 1.2f;
|
||||
|
||||
public static final int EXIT_INDEX = -2;
|
||||
public static final int ENTER_INDEX = -3;
|
||||
@@ -130,10 +130,8 @@ public class ClippedFolderIconLayoutRule {
|
||||
public float scaleForItem(int numItems) {
|
||||
// Scale is determined by the number of items in the preview.
|
||||
final float scale;
|
||||
if (numItems <= 2) {
|
||||
if (numItems <= 3) {
|
||||
scale = MAX_SCALE;
|
||||
} else if (numItems == 3) {
|
||||
scale = (MAX_SCALE + MIN_SCALE) / 2;
|
||||
} else {
|
||||
scale = MIN_SCALE;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.graphics.Insets;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.InputType;
|
||||
@@ -67,6 +68,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
@@ -250,6 +252,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
// so that we can cancel it when starting mColorChangeAnimator.
|
||||
private ObjectAnimator mOpenAnimationColorChangeAnimator;
|
||||
|
||||
private GradientDrawable mBackground;
|
||||
|
||||
/**
|
||||
* Used to inflate the Workspace from XML.
|
||||
*
|
||||
@@ -268,6 +272,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
// name is complete, we have something to focus on, thus hiding the cursor and giving
|
||||
// reliable behavior when clicking the text field (since it will always gain focus on click).
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getBackground() {
|
||||
return mBackground;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -276,6 +286,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
final DeviceProfile dp = mActivityContext.getDeviceProfile();
|
||||
final int paddingLeftRight = dp.folderContentPaddingLeftRight;
|
||||
|
||||
mBackground = (GradientDrawable) ResourcesCompat.getDrawable(getResources(),
|
||||
R.drawable.round_rect_folder, getContext().getTheme());
|
||||
|
||||
mContent = findViewById(R.id.folder_content);
|
||||
mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
|
||||
mContent.setFolder(this);
|
||||
@@ -1213,6 +1226,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
lp.x = left;
|
||||
lp.y = top;
|
||||
|
||||
mBackground.setBounds(0, 0, width, height);
|
||||
|
||||
if (mColorExtractor != null) {
|
||||
mColorExtractor.removeLocations();
|
||||
mColorExtractor.setListener(mColorListener);
|
||||
@@ -1714,14 +1729,16 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
if (mClipPath != null) {
|
||||
int count = canvas.save();
|
||||
canvas.clipPath(mClipPath);
|
||||
super.draw(canvas);
|
||||
mBackground.draw(canvas);
|
||||
canvas.restoreToCount(count);
|
||||
super.dispatchDraw(canvas);
|
||||
} else {
|
||||
super.draw(canvas);
|
||||
mBackground.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.view.animation.AnimationUtils;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -80,6 +81,8 @@ public class FolderAnimationManager {
|
||||
|
||||
private ObjectAnimator mBgColorAnimator;
|
||||
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
public FolderAnimationManager(Folder folder, boolean isOpening) {
|
||||
mFolder = folder;
|
||||
mContent = folder.mContent;
|
||||
@@ -89,7 +92,8 @@ public class FolderAnimationManager {
|
||||
mPreviewBackground = mFolderIcon.mBackground;
|
||||
|
||||
mContext = folder.getContext();
|
||||
mPreviewVerifier = new FolderGridOrganizer(folder.mActivityContext.getDeviceProfile().inv);
|
||||
mDeviceProfile = folder.mActivityContext.getDeviceProfile();
|
||||
mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile.inv);
|
||||
|
||||
mIsOpening = isOpening;
|
||||
|
||||
@@ -211,8 +215,21 @@ public class FolderAnimationManager {
|
||||
play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
|
||||
play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
|
||||
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
|
||||
|
||||
// Create reveal animator for the folder background
|
||||
play(a, getShape().createRevealAnimator(
|
||||
mFolder, startRect, endRect, finalRadius, !mIsOpening));
|
||||
|
||||
// Create reveal animator for the folder content (capture the top 4 icons 2x2)
|
||||
int width = mContent.getPaddingLeft() + mDeviceProfile.folderCellLayoutBorderSpacingPx
|
||||
+ mDeviceProfile.folderCellWidthPx * 2;
|
||||
int height = mContent.getPaddingTop() + mDeviceProfile.folderCellLayoutBorderSpacingPx
|
||||
+ mDeviceProfile.folderCellHeightPx * 2;
|
||||
Rect startRect2 = new Rect(0, 0, width, height);
|
||||
play(a, getShape().createRevealAnimator(
|
||||
mFolder.getContent(), startRect2, endRect, finalRadius, !mIsOpening));
|
||||
|
||||
|
||||
// Fade in the folder name, as the text can overlap the icons when grid size is small.
|
||||
mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
|
||||
play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
|
||||
|
||||
@@ -614,10 +614,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
|
||||
if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
|
||||
|
||||
final int saveCount = canvas.save();
|
||||
canvas.clipPath(mBackground.getClipPath());
|
||||
mPreviewItemManager.draw(canvas);
|
||||
canvas.restoreToCount(saveCount);
|
||||
|
||||
if (!mBackground.drawingDelegated()) {
|
||||
mBackground.drawBackgroundStroke(canvas);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
@@ -49,6 +50,7 @@ import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.util.ViewCache;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.ClipPathView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -57,7 +59,7 @@ import java.util.Map;
|
||||
import java.util.function.ToIntFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FolderPagedView extends PagedView<PageIndicatorDots> {
|
||||
public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
|
||||
|
||||
private static final String TAG = "FolderPagedView";
|
||||
|
||||
@@ -89,6 +91,8 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
|
||||
|
||||
private Folder mFolder;
|
||||
|
||||
private Path mClipPath;
|
||||
|
||||
// If the views are attached to the folder or not. A folder should be bound when its
|
||||
// animating or is open.
|
||||
private boolean mViewsBound = false;
|
||||
@@ -128,8 +132,16 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
mFocusIndicatorHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
if (mClipPath != null) {
|
||||
int count = canvas.save();
|
||||
canvas.clipPath(mClipPath);
|
||||
mFocusIndicatorHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
canvas.restoreToCount(count);
|
||||
} else {
|
||||
mFocusIndicatorHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,4 +640,10 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
|
||||
public int itemsPerPage() {
|
||||
return mOrganizer.getMaxItemsPerPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClipPath(Path clipPath) {
|
||||
mClipPath = clipPath;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.android.launcher3.views.ActivityContext;
|
||||
public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
|
||||
|
||||
private static final boolean DRAW_SHADOW = false;
|
||||
private static final boolean DRAW_STROKE = false;
|
||||
|
||||
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
|
||||
|
||||
@@ -303,6 +304,10 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
|
||||
}
|
||||
|
||||
public void animateBackgroundStroke() {
|
||||
if (!DRAW_STROKE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mStrokeAlphaAnimator != null) {
|
||||
mStrokeAlphaAnimator.cancel();
|
||||
}
|
||||
@@ -319,6 +324,9 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
|
||||
}
|
||||
|
||||
public void drawBackgroundStroke(Canvas canvas) {
|
||||
if (!DRAW_STROKE) {
|
||||
return;
|
||||
}
|
||||
mPaint.setColor(setColorAlphaBound(mStrokeColor, mStrokeAlpha));
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(mStrokeWidth);
|
||||
@@ -363,7 +371,7 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
|
||||
}
|
||||
|
||||
mDrawingDelegate = null;
|
||||
isClipping = true;
|
||||
isClipping = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -156,19 +156,43 @@ public abstract class IconShape {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Circle extends SimpleRectShape {
|
||||
public static final class Circle extends PathShape {
|
||||
|
||||
@Override
|
||||
public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
|
||||
canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
|
||||
private final float[] mTempRadii = new float[8];
|
||||
|
||||
protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
|
||||
float endRadius, Path outPath) {
|
||||
float r1 = getStartRadius(startRect);
|
||||
|
||||
float[] startValues = new float[] {
|
||||
startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r1};
|
||||
float[] endValues = new float[] {
|
||||
endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
|
||||
|
||||
FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
|
||||
|
||||
return (anim) -> {
|
||||
float progress = (Float) anim.getAnimatedValue();
|
||||
float[] values = evaluator.evaluate(progress, startValues, endValues);
|
||||
outPath.addRoundRect(
|
||||
values[0], values[1], values[2], values[3],
|
||||
getRadiiArray(values[4], values[5]), Path.Direction.CW);
|
||||
};
|
||||
}
|
||||
|
||||
private float[] getRadiiArray(float r1, float r2) {
|
||||
mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
|
||||
mTempRadii[6] = mTempRadii[7] = r1;
|
||||
mTempRadii[4] = mTempRadii[5] = r2;
|
||||
return mTempRadii;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addToPath(Path path, float offsetX, float offsetY, float radius) {
|
||||
path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getStartRadius(Rect startRect) {
|
||||
return startRect.width() / 2f;
|
||||
}
|
||||
|
||||
@@ -32,13 +32,44 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
|
||||
|
||||
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
|
||||
|
||||
/**
|
||||
* The desired number of cells that this widget occupies horizontally in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int spanX;
|
||||
|
||||
/**
|
||||
* The desired number of cells that this widget occupies vertically in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int spanY;
|
||||
|
||||
/**
|
||||
* The minimum number of cells that this widget can occupy horizontally in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int minSpanX;
|
||||
|
||||
/**
|
||||
* The minimum number of cells that this widget can occupy vertically in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int minSpanY;
|
||||
|
||||
/**
|
||||
* The maximum number of cells that this widget can occupy horizontally in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int maxSpanX;
|
||||
|
||||
/**
|
||||
* The maximum number of cells that this widget can occupy vertically in
|
||||
* {@link com.android.launcher3.CellLayout}.
|
||||
*/
|
||||
public int maxSpanY;
|
||||
|
||||
private boolean mIsMinSizeFulfilled;
|
||||
|
||||
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
|
||||
AppWidgetProviderInfo info) {
|
||||
final LauncherAppWidgetProviderInfo launcherInfo;
|
||||
@@ -133,8 +164,20 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
|
||||
this.minSpanY = minSpanY;
|
||||
this.maxSpanX = maxSpanX;
|
||||
this.maxSpanY = maxSpanY;
|
||||
this.spanX = spanX;
|
||||
this.spanY = spanY;
|
||||
this.mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns
|
||||
&& Math.min(spanY, minSpanY) <= idp.numRows;
|
||||
// Ensures the default span X and span Y will not exceed the current grid size.
|
||||
this.spanX = Math.min(spanX, idp.numColumns);
|
||||
this.spanY = Math.min(spanY, idp.numRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the widget's minimum size requirement can be fulfilled in the device
|
||||
* grid setting, {@link InvariantDeviceProfile}, that was passed in
|
||||
* {@link #initSpans(Context, InvariantDeviceProfile)}.
|
||||
*/
|
||||
public boolean isMinSizeFulfilled() {
|
||||
return mIsMinSizeFulfilled;
|
||||
}
|
||||
|
||||
private int getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth) {
|
||||
|
||||
@@ -202,11 +202,16 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
|
||||
}
|
||||
|
||||
/** Returns whether {@code entry} matches {@link #mWidgetsContentVisiblePackageUserKey}. */
|
||||
private boolean isHeaderForVisibleContent(WidgetsListBaseEntry entry) {
|
||||
return isHeaderForPackageUserKey(entry, mWidgetsContentVisiblePackageUserKey);
|
||||
}
|
||||
|
||||
/** Returns whether {@code entry} matches {@code key}. */
|
||||
private boolean isHeaderForPackageUserKey(WidgetsListBaseEntry entry, PackageUserKey key) {
|
||||
return (entry instanceof WidgetsListHeaderEntry
|
||||
|| entry instanceof WidgetsListSearchHeaderEntry)
|
||||
&& new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
|
||||
.equals(mWidgetsContentVisiblePackageUserKey);
|
||||
&& new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user).equals(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,36 +275,68 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
|
||||
@Override
|
||||
public void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey) {
|
||||
// Ignore invalid clicks, such as collapsing a package that isn't currently expanded.
|
||||
if (!showWidgets && !packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
|
||||
|
||||
if (showWidgets) {
|
||||
mWidgetsContentVisiblePackageUserKey = packageUserKey;
|
||||
updateVisibleEntries();
|
||||
// Scroll the layout manager to the header position to keep it anchored to the same
|
||||
// position.
|
||||
scrollToPositionAndMaintainOffset(getSelectedHeaderPosition());
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
|
||||
} else if (packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) {
|
||||
OptionalInt previouslySelectedPosition = getSelectedHeaderPosition();
|
||||
|
||||
} else {
|
||||
mWidgetsContentVisiblePackageUserKey = null;
|
||||
updateVisibleEntries();
|
||||
|
||||
// Scroll to the header that was just collapsed so it maintains its scroll offset.
|
||||
scrollToPositionAndMaintainOffset(previouslySelectedPosition);
|
||||
}
|
||||
|
||||
// Get the current top of the header with the matching key before adjusting the visible
|
||||
// entries.
|
||||
OptionalInt topForPackageUserKey =
|
||||
getOffsetForPosition(getPositionForPackageUserKey(packageUserKey));
|
||||
|
||||
updateVisibleEntries();
|
||||
|
||||
// Get the position for the clicked header after adjusting the visible entries. The
|
||||
// position may have changed if another header had previously been expanded.
|
||||
OptionalInt positionForPackageUserKey = getPositionForPackageUserKey(packageUserKey);
|
||||
scrollToPositionAndMaintainOffset(positionForPackageUserKey, topForPackageUserKey);
|
||||
}
|
||||
|
||||
private OptionalInt getSelectedHeaderPosition() {
|
||||
/**
|
||||
* Returns the position of {@code key} in {@link #mVisibleEntries}, or empty if it's not
|
||||
* present.
|
||||
*/
|
||||
private OptionalInt getPositionForPackageUserKey(PackageUserKey key) {
|
||||
return IntStream.range(0, mVisibleEntries.size())
|
||||
.filter(index -> isHeaderForVisibleContent(mVisibleEntries.get(index)))
|
||||
.filter(index -> isHeaderForPackageUserKey(mVisibleEntries.get(index), key))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to the selected header position. LinearLayoutManager scrolls the minimum distance
|
||||
* necessary, so this will keep the selected header in place during clicks, without interrupting
|
||||
* the animation.
|
||||
* Returns the top of {@code positionOptional} in the recycler view, or empty if its view
|
||||
* can't be found for any reason, including the position not being currently visible. The
|
||||
* returned value does not include the top padding of the recycler view.
|
||||
*/
|
||||
private void scrollToPositionAndMaintainOffset(OptionalInt positionOptional) {
|
||||
private OptionalInt getOffsetForPosition(OptionalInt positionOptional) {
|
||||
if (!positionOptional.isPresent() || mRecyclerView == null) return OptionalInt.empty();
|
||||
|
||||
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (layoutManager == null) return OptionalInt.empty();
|
||||
|
||||
View view = layoutManager.findViewByPosition(positionOptional.getAsInt());
|
||||
if (view == null) return OptionalInt.empty();
|
||||
|
||||
return OptionalInt.of(layoutManager.getDecoratedTop(view));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to the selected header position with the provided offset. LinearLayoutManager
|
||||
* scrolls the minimum distance necessary, so this will keep the selected header in place during
|
||||
* clicks, without interrupting the animation.
|
||||
*
|
||||
* @param positionOptional The position too scroll to. No scrolling will be done if empty.
|
||||
* @param offsetOptional The offset from the top to maintain. If empty, then the list will
|
||||
* scroll to the top of the position.
|
||||
*/
|
||||
private void scrollToPositionAndMaintainOffset(
|
||||
OptionalInt positionOptional,
|
||||
OptionalInt offsetOptional) {
|
||||
if (!positionOptional.isPresent() || mRecyclerView == null) return;
|
||||
int position = positionOptional.getAsInt();
|
||||
|
||||
@@ -317,12 +354,9 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
|
||||
// Scroll to the header view's current offset, accounting for the recycler view's padding.
|
||||
// If the header view couldn't be found, then it will appear at the top of the list.
|
||||
View headerView = layoutManager.findViewByPosition(position);
|
||||
int targetHeaderViewTop =
|
||||
headerView == null ? 0 : layoutManager.getDecoratedTop(headerView);
|
||||
layoutManager.scrollToPositionWithOffset(
|
||||
position,
|
||||
targetHeaderViewTop - mRecyclerView.getPaddingTop());
|
||||
offsetOptional.orElse(0) - mRecyclerView.getPaddingTop());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -254,13 +254,11 @@ public class WidgetsModel {
|
||||
}
|
||||
|
||||
// Ensure that all widgets we show can be added on a workspace of this size
|
||||
int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
|
||||
int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
|
||||
if (minSpanX > mIdp.numColumns || minSpanY > mIdp.numRows) {
|
||||
if (!item.widgetInfo.isMinSizeFulfilled()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format(
|
||||
"Widget %s : (%d X %d) can't fit on this device",
|
||||
item.componentName, minSpanX, minSpanY));
|
||||
"Widget %s : can't fit on this device with a grid size: %dx%d",
|
||||
item.componentName, mIdp.numColumns, mIdp.numRows));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user