diff --git a/Android.bp b/Android.bp index 8b7eb54b9e..60ef5b1ab7 100644 --- a/Android.bp +++ b/Android.bp @@ -204,6 +204,7 @@ android_library { ], static_libs: [ "Launcher3ResLib", + "lottie", "SystemUISharedLib", "SystemUI-statsd", ], diff --git a/build.gradle b/build.gradle index 617738a5ed..683a4cfaf1 100644 --- a/build.gradle +++ b/build.gradle @@ -163,6 +163,8 @@ dependencies { androidTestImplementation 'com.android.support.test:rules:1.0.0' androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}" + + api 'com.airbnb.android:lottie:3.3.0' } protobuf { diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml index b4ee4828a6..9ad10dc54e 100644 --- a/quickstep/res/layout/activity_allset.xml +++ b/quickstep/res/layout/activity_allset.xml @@ -25,82 +25,100 @@ + android:fitsSystemWindows="true"> - - - + android:layout_height="match_parent" + android:gravity="center" + android:scaleType="centerCrop" - + app:lottie_rawRes="@raw/all_set_page_bg" + app:lottie_autoPlay="true" + app:lottie_loop="true" /> - + - + - + + + + + + + + + + + + - \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index e48c9e8b6b..4e6b7b99df 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -28,6 +28,7 @@ 4dp 2dp 234dp + 8dp 48dp 16dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index 8c6185cb0f..81039d4d58 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -143,8 +143,7 @@ public class TaskbarDragLayerController { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } else if (mControllers.navbarButtonsViewController.isImeVisible()) { - insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT); - insetsIsTouchableRegion = false; + insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } else if (!mControllers.uiController.isTaskbarTouchable()) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java index 57dad08456..a768ef5253 100644 --- a/quickstep/src/com/android/quickstep/KtR.java +++ b/quickstep/src/com/android/quickstep/KtR.java @@ -30,6 +30,7 @@ public class KtR { public static final class dimen { public static int task_menu_spacing = R.dimen.task_menu_spacing; + public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding; } public static final class layout { diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index c45159e48e..02468497bb 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -208,13 +208,6 @@ public class TaskOverlayFactory implements ResourceBasedOverride { } } - /** - * Called when the current task's thumbnail has changed. - */ - public void refreshActionVisibility(ThumbnailData thumbnail) { - getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null); - } - /** * End rendering live tile in Overview. * diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index eaa43cfd9c..3175ba8463 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -134,7 +134,8 @@ public class TaskThumbnailCache { Preconditions.assertUIThread(); boolean lowResolution = !mHighResLoadingState.isEnabled(); - if (task.thumbnail != null && (!task.thumbnail.reducedResolution || lowResolution)) { + if (task.thumbnail != null && task.thumbnail.thumbnail != null + && (!task.thumbnail.reducedResolution || lowResolution)) { // Nothing to load, the thumbnail is already high-resolution or matches what the // request, so just callback callback.accept(task.thumbnail); @@ -152,7 +153,8 @@ public class TaskThumbnailCache { Preconditions.assertUIThread(); ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key); - if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || lowResolution)) { + if (cachedThumbnail != null && cachedThumbnail.thumbnail != null + && (!cachedThumbnail.reducedResolution || lowResolution)) { // Already cached, lets use that thumbnail callback.accept(cachedThumbnail); return null; diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index ae3cc50ac6..6addfe30f4 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -32,7 +32,6 @@ import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURA import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.statehandlers.DepthController.DEPTH; @@ -193,7 +192,6 @@ public final class TaskViewUtils { boolean showAsGrid = dp.overviewShowAsGrid; boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage() && !showAsGrid; - float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex); int startScroll = recentsView.getScrollOffset(taskIndex); RemoteTargetHandle[] topMostSimulators = null; @@ -211,11 +209,9 @@ public final class TaskViewUtils { tvsLocal.fullScreenProgress.value = 0; tvsLocal.recentsViewScale.value = 1; - if (showAsGrid) { - tvsLocal.taskSecondaryTranslation.value = gridTranslationSecondary; - } tvsLocal.setScroll(startScroll); tvsLocal.setIsGridTask(v.isGridTask()); + tvsLocal.setGridTranslationY(v.getGridTranslationY()); // Fade in the task during the initial 20% of the animation out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1, @@ -230,10 +226,6 @@ public final class TaskViewUtils { out.setFloat(tvsLocal.recentsViewScale, AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR); - if (showAsGrid) { - out.setFloat(tvsLocal.taskSecondaryTranslation, AnimatedFloat.VALUE, 0, - TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL); - } out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index eff59e24bc..95095fa8eb 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -16,6 +16,7 @@ package com.android.quickstep.fallback; import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; +import static com.android.quickstep.ViewUtils.postFrameDrawn; import static com.android.quickstep.fallback.RecentsState.DEFAULT; import static com.android.quickstep.fallback.RecentsState.HOME; import static com.android.quickstep.fallback.RecentsState.MODAL_TASK; @@ -218,8 +219,14 @@ public class FallbackRecentsView extends RecentsView runActionOnRemoteHandles(remoteTargetHandle -> + remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true))); + } } @Override diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 965c1bcc32..1c3e784308 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -19,6 +19,7 @@ import static com.android.launcher3.Utilities.mapBoundToRange; import static com.android.launcher3.Utilities.mapRange; import static com.android.launcher3.anim.Interpolators.LINEAR; +import android.animation.Animator; import android.app.Activity; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; @@ -38,6 +39,8 @@ import android.graphics.Rect; import android.graphics.Shader.TileMode; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.util.Log; import android.view.View; import android.view.View.AccessibilityDelegate; @@ -56,6 +59,8 @@ import com.android.quickstep.GestureState; import com.android.quickstep.TouchInteractionService.TISBinder; import com.android.quickstep.util.TISBindHelper; +import com.airbnb.lottie.LottieAnimationView; + import java.net.URISyntaxException; /** @@ -80,6 +85,10 @@ public class AllSetActivity extends Activity { private View mContentView; private float mSwipeUpShift; + @Nullable private Vibrator mVibrator; + private LottieAnimationView mAnimatedBackground; + private Animator.AnimatorListener mBackgroundAnimatorListener; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -115,6 +124,52 @@ public class AllSetActivity extends Activity { findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate()); mTISBindHelper = new TISBindHelper(this, this::onTISConnected); + + mVibrator = getSystemService(Vibrator.class); + mAnimatedBackground = findViewById(R.id.animated_background); + startBackgroundAnimation(); + } + + private void startBackgroundAnimation() { + if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_THUD)) { + if (mBackgroundAnimatorListener == null) { + mBackgroundAnimatorListener = + new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mVibrator.vibrate(getVibrationEffect()); + } + + @Override + public void onAnimationRepeat(Animator animation) { + mVibrator.vibrate(getVibrationEffect()); + } + + @Override + public void onAnimationEnd(Animator animation) { + mVibrator.cancel(); + } + + @Override + public void onAnimationCancel(Animator animation) { + mVibrator.cancel(); + } + }; + } + mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener); + } + mAnimatedBackground.playAnimation(); + } + + /** + * Sets up the vibration effect for the next round of animation. The parameters vary between + * different illustrations. + */ + private VibrationEffect getVibrationEffect() { + return VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50) + .compose(); } @Override @@ -153,6 +208,9 @@ public class AllSetActivity extends Activity { super.onDestroy(); mTISBindHelper.onDestroy(); clearBinderOverride(); + if (mBackgroundAnimatorListener != null) { + mAnimatedBackground.removeAnimatorListener(mBackgroundAnimatorListener); + } } private AnimatedFloat createSwipeUpProxy(GestureState state) { @@ -173,6 +231,12 @@ public class AllSetActivity extends Activity { 1, 0, LINEAR); mContentView.setAlpha(alpha); mContentView.setTranslationY((alpha - 1) * mSwipeUpShift); + + if (alpha == 0f) { + mAnimatedBackground.pauseAnimation(); + } else if (!mAnimatedBackground.isAnimating()) { + mAnimatedBackground.resumeAnimation(); + } } /** diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 146d2355f1..7d396ba002 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -102,6 +102,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private StagedSplitBounds mStagedSplitBounds; private boolean mDrawsBelowRecents; private boolean mIsGridTask; + private float mGridTranslationY; public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) { mContext = context; @@ -156,9 +157,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { fullTaskSize = new Rect(mTaskRect); mOrientationState.getOrientationHandler() .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition); + if (mIsGridTask) { + mTaskRect.offset(0, (int) mGridTranslationY); + } } else { fullTaskSize = mTaskRect; } + if (mIsGridTask) { + fullTaskSize.offset(0, (int) mGridTranslationY); + } return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot); } @@ -217,6 +224,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mIsGridTask = isGridTask; } + /** + * Sets the y-translation when overview is in grid. + */ + public void setGridTranslationY(float gridTranslationY) { + mGridTranslationY = gridTranslationY; + } + /** * Adds animation for all the components corresponding to transition from an app to overview. */ @@ -320,14 +334,12 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mMatrix.postTranslate(insets.left, insets.top); mMatrix.postScale(scale, scale); - // Apply TaskView matrix: translate, scroll + // Apply TaskView matrix: taskRect, translate mMatrix.postTranslate(mTaskRect.left, mTaskRect.top); mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE, taskPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, taskSecondaryTranslation.value); - mOrientationState.getOrientationHandler().set( - mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value); // Apply RecentsView matrix mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y); @@ -335,6 +347,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { recentsViewSecondaryTranslation.value); mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE, recentsViewPrimaryTranslation.value); + mOrientationState.getOrientationHandler().set( + mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value); applyWindowToHomeRotation(mMatrix); // Crop rect is the inverse of thumbnail matrix diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index df99d27376..ea83b4deaf 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -4,7 +4,6 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; import android.content.Context; -import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; @@ -50,8 +49,6 @@ public class GroupedTaskView extends TaskView { private final float[] mIcon2CenterCoords = new float[2]; private TransformingTouchDelegate mIcon2TouchDelegate; @Nullable private StagedSplitBounds mSplitBoundsConfig; - private final Rect mPrimaryTempRect = new Rect(); - private final Rect mSecondaryTempRect = new Rect(); public GroupedTaskView(Context context) { super(context); @@ -239,10 +236,8 @@ public class GroupedTaskView extends TaskView { int taskIconHeight = deviceProfile.overviewTaskIconSizePx; boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; - mSnapshotView.getBoundsOnScreen(mPrimaryTempRect); - mSnapshotView2.getBoundsOnScreen(mSecondaryTempRect); getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2, - taskIconHeight, mPrimaryTempRect, mSecondaryTempRect, + taskIconHeight, mSnapshotView.getWidth(), mSnapshotView.getHeight(), isRtl, deviceProfile, mSplitBoundsConfig); } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 5d6b6563f7..3cba3921b2 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.LauncherState.SPRING_LOADED; +import static com.android.quickstep.ViewUtils.postFrameDrawn; import android.annotation.TargetApi; import android.content.Context; @@ -104,8 +105,14 @@ public class LauncherRecentsView extends RecentsView runActionOnRemoteHandles(remoteTargetHandle -> + remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true))); + } } @Override diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index d1a4a13fe7..7b5a6b9401 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1508,17 +1508,6 @@ public abstract class RecentsView { - TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator(); - simulator.taskPrimaryTranslation.value = 0; - simulator.taskSecondaryTranslation.value = 0; - simulator.fullScreenProgress.value = 0; - simulator.recentsViewScale.value = 1; - }); - // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is // null. if (!mRunningTaskShowScreenshot) { @@ -1906,7 +1895,7 @@ public abstract class RecentsView { remoteTargetHandle.getTransformParams().setTargetSet(null); - remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true); + remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false); }); mSplitSelectStateController.resetState(); @@ -4389,7 +4378,6 @@ public abstract class RecentsView getEventDispatcher(float navbarRotation) { float degreesRotated; if (navbarRotation == 0) { diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt index 5059f8b532..179fd68361 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt +++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt @@ -23,14 +23,18 @@ import android.graphics.Rect import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.RectShape import android.util.AttributeSet +import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.LinearLayout import com.android.launcher3.BaseDraggingActivity import com.android.launcher3.DeviceProfile +import com.android.launcher3.InsettableFrameLayout import com.android.launcher3.R import com.android.launcher3.popup.ArrowPopup +import com.android.launcher3.popup.RoundedArrowDrawable import com.android.launcher3.popup.SystemShortcut import com.android.launcher3.util.Themes import com.android.quickstep.KtR @@ -43,9 +47,13 @@ class TaskMenuViewWithArrow : ArrowPopup { fun showForTask(taskContainer: TaskIdAttributeContainer): Boolean { val activity = BaseDraggingActivity - .fromContext(taskContainer.taskView.context) + .fromContext(taskContainer.taskView.context) val taskMenuViewWithArrow = activity.layoutInflater - .inflate(KtR.layout.task_menu_with_arrow, activity.dragLayer, false) as TaskMenuViewWithArrow<*> + .inflate( + KtR.layout.task_menu_with_arrow, + activity.dragLayer, + false + ) as TaskMenuViewWithArrow<*> return taskMenuViewWithArrow.populateAndShowForTask(taskContainer) } @@ -53,10 +61,21 @@ class TaskMenuViewWithArrow : ArrowPopup { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) init { clipToOutline = true + + shouldScaleArrow = true + // This synchronizes the arrow and menu to open at the same time + OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY + OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION + CLOSE_FADE_START_DELAY = CLOSE_CHILD_FADE_START_DELAY + CLOSE_FADE_DURATION = CLOSE_CHILD_FADE_DURATION } private val menuWidth = context.resources.getDimensionPixelSize(R.dimen.task_menu_width_grid) @@ -65,6 +84,13 @@ class TaskMenuViewWithArrow : ArrowPopup { private lateinit var optionLayout: LinearLayout private lateinit var taskContainer: TaskIdAttributeContainer + private var optionMeasuredHeight = 0 + private val arrowHorizontalPadding: Int + get() = if (taskView.isFocusedTask) + resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding) + else + 0 + override fun isOfType(type: Int): Boolean = type and TYPE_TASK_MENU != 0 override fun getTargetObjectLocation(outPos: Rect?) { @@ -147,7 +173,10 @@ class TaskMenuViewWithArrow : ArrowPopup { } override fun assignMarginsAndBackgrounds(viewGroup: ViewGroup) { - assignMarginsAndBackgrounds(this, Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)) + assignMarginsAndBackgrounds( + this, + Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface) + ) } override fun onCreateOpenAnimation(anim: AnimatorSet) { @@ -164,4 +193,90 @@ class TaskMenuViewWithArrow : ArrowPopup { ObjectAnimator.ofFloat(taskContainer.thumbnailView, TaskThumbnailView.DIM_ALPHA, 0f) ) } + + /** + * Orients this container to the left or right of the given icon, aligning with the first option + * or second. + * + * These are the preferred orientations, in order (RTL prefers right-aligned over left): + * - Right and first option aligned + * - Right and second option aligned + * - Left and first option aligned + * - Left and second option aligned + * + * So we always align right if there is enough horizontal space + */ + override fun orientAboutObject() { + measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED) + // Needed for offsets later + optionMeasuredHeight = optionLayout.getChildAt(0).measuredHeight + val extraHorizontalSpace = (mArrowHeight + mArrowOffsetVertical + arrowHorizontalPadding) + + val widthWithArrow = measuredWidth + paddingLeft + paddingRight + extraHorizontalSpace + getTargetObjectLocation(mTempRect) + val dragLayer: InsettableFrameLayout = popupContainer + val insets = dragLayer.insets + + // Put to the right of the icon if there is space, which means left aligned with the menu + val rightAlignedMenuStartX = mTempRect.left - widthWithArrow + val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace + mIsLeftAligned = if (mIsRtl) { + rightAlignedMenuStartX + insets.left < 0 + } else { + leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left < + dragLayer.width - insets.right + } + + var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX + + // Offset y so that the arrow and first row are center-aligned with the original icon. + val iconHeight = mTempRect.height() + val optionHeight = optionMeasuredHeight + val yOffset = (optionHeight - iconHeight) / 2 + var menuStartY = mTempRect.top - yOffset + + // Insets are added later, so subtract them now. + menuStartX -= insets.left + menuStartY -= insets.top + + setX(menuStartX.toFloat()) + setY(menuStartY.toFloat()) + + val lp = layoutParams as FrameLayout.LayoutParams + val arrowLp = mArrow.layoutParams as FrameLayout.LayoutParams + lp.gravity = Gravity.TOP + arrowLp.gravity = lp.gravity + } + + override fun addArrow() { + popupContainer.addView(mArrow) + mArrow.x = getArrowX() + mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) + + updateArrowColor() + + // This is inverted (x = height, y = width) because the arrow is rotated + mArrow.pivotX = if (mIsLeftAligned) 0f else mArrowHeight.toFloat() + mArrow.pivotY = 0f + } + + private fun getArrowX(): Float { + return if (mIsLeftAligned) + x - mArrowHeight + else + x + measuredWidth + mArrowOffsetVertical + } + + override fun updateArrowColor() { + mArrow.background = RoundedArrowDrawable( + mArrowWidth.toFloat(), + mArrowHeight.toFloat(), + mArrowPointRadius.toFloat(), + mIsLeftAligned, + mArrowColor + ) + elevation = mElevation + mArrow.elevation = mElevation + } + } \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index f8368aed6c..d91669ad96 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -148,10 +148,11 @@ public class TaskThumbnailView extends View { public void setThumbnail(@Nullable Task task, @Nullable ThumbnailData thumbnailData, boolean refreshNow) { mTask = task; + boolean thumbnailWasNull = mThumbnailData == null; mThumbnailData = (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null; if (refreshNow) { - refresh(); + refresh(thumbnailWasNull && mThumbnailData != null); } } @@ -162,14 +163,22 @@ public class TaskThumbnailView extends View { /** Updates the shader, paint, matrix to redraw. */ public void refresh() { + refresh(false); + } + + /** + * Updates the shader, paint, matrix to redraw. + * @param shouldRefreshOverlay whether to re-initialize overlay + */ + private void refresh(boolean shouldRefreshOverlay) { if (mThumbnailData != null && mThumbnailData.thumbnail != null) { Bitmap bm = mThumbnailData.thumbnail; bm.prepareToDraw(); mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint.setShader(mBitmapShader); updateThumbnailMatrix(); - if (mOverlayEnabled) { - getTaskOverlay().refreshActionVisibility(mThumbnailData); + if (shouldRefreshOverlay) { + refreshOverlay(); } } else { mBitmapShader = null; diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 3da789352a..67128f01fe 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -648,7 +648,7 @@ public class TaskView extends FrameLayout implements Reusable { recentsView.getDepthController()); anim.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationStart(Animator animator) { + public void onAnimationStart(Animator animation) { recentsView.runActionOnRemoteHandles( (Consumer) remoteTargetHandle -> remoteTargetHandle @@ -658,11 +658,6 @@ public class TaskView extends FrameLayout implements Reusable { @Override public void onAnimationEnd(Animator animator) { - recentsView.runActionOnRemoteHandles( - (Consumer) remoteTargetHandle -> - remoteTargetHandle - .getTaskViewSimulator() - .setDrawsBelowRecents(true)); mIsClickableAsLiveTile = true; } }); diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml index b27b50560e..0f6fc6cdb7 100644 --- a/res/layout/widget_cell_content.xml +++ b/res/layout/widget_cell_content.xml @@ -33,6 +33,14 @@ android:layout_height="match_parent" android:importantForAccessibility="no" android:layout_gravity="fill"/> + + diff --git a/res/raw/all_set_page_bg.json b/res/raw/all_set_page_bg.json new file mode 100644 index 0000000000..9705837912 --- /dev/null +++ b/res/raw/all_set_page_bg.json @@ -0,0 +1 @@ +{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]} diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 3758093ebd..0235ef0c2d 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -36,8 +36,8 @@ "Tocca e tieni premuto il widget per spostarlo nella schermata Home" "Aggiungi a schermata Home" "Widget %1$s aggiunto alla schermata Home" - "{count,plural, =1{# widget}other{# widget}}" - "{count,plural, =1{# scorciatoia}other{# scorciatoie}}" + "{count,plural, =1{# widget}one{# widget}other{# widget}}" + "{count,plural, =1{# scorciatoia}one{# scorciatoia}other{# scorciatoie}}" "%1$s, %2$s" "Widget" "Cerca" @@ -86,7 +86,7 @@ "Questa è un\'app di sistema e non può essere disinstallata." "Modifica nome" "App %1$s disattivata" - "{count,plural,offset:1 =1{{app_name} ha # notifica}other{{app_name} ha # notifiche}}" + "{count,plural,offset:1 =1{{app_name} ha # notifica}one{{app_name} ha # notifica}other{{app_name} ha # notifiche}}" "Pagina %1$d di %2$d" "Schermata Home %1$d di %2$d" "Nuova pagina Schermata Home" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 09a12007e1..a3c225a257 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -36,8 +36,8 @@ "Toque sem soltar no widget para o mover à volta do ecrã principal" "Adicionar ao ecrã principal" "Widget %1$s adicionado ao ecrã principal" - "{count,plural, =1{# widget}other{# widgets}}" - "{count,plural, =1{# atalho}other{# atalhos}}" + "{count,plural, =1{# widget}one{# widget(s)}other{# widgets}}" + "{count,plural, =1{# atalho}one{# atalho(s)}other{# atalhos}}" "%1$s, %2$s" "Widgets" "Pesquisar" @@ -86,7 +86,7 @@ "É uma app de sistema e não pode ser desinstalada." "Edite o nome" "%1$s desativado" - "{count,plural,offset:1 =1{A app {app_name} tem # notificação}other{A app {app_name} tem # notificações}}" + "{count,plural,offset:1 =1{A app {app_name} tem # notificação}one{A app {app_name} tem # notificação(ões)}other{A app {app_name} tem # notificações}}" "Página %1$d de %2$d" "Ecrã principal %1$d de %2$d" "Nova página do ecrã principal" diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 02eb1dea6f..adb1613e9d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -19,6 +19,8 @@ package com.android.launcher3; import static android.animation.ValueAnimator.areAnimatorsEnabled; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; +import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON; +import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -37,7 +39,6 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Parcelable; import android.util.ArrayMap; @@ -61,6 +62,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.CellAndSpan; @@ -442,18 +444,43 @@ public class CellLayout extends ViewGroup { } if (DEBUG_VISUALIZE_OCCUPIED) { - int[] pt = new int[2]; - ColorDrawable cd = new ColorDrawable(Color.RED); - cd.setBounds(0, 0, mCellWidth, mCellHeight); - for (int i = 0; i < mCountX; i++) { - for (int j = 0; j < mCountY; j++) { - if (mOccupied.cells[i][j]) { - cellToPoint(i, j, pt); - canvas.save(); - canvas.translate(pt[0], pt[1]); - cd.draw(canvas); - canvas.restore(); + Rect cellBounds = new Rect(); + // Will contain the bounds of the cell including spacing between cells. + Rect cellBoundsWithSpacing = new Rect(); + int[] targetCell = new int[2]; + int[] cellCenter = new int[2]; + Paint debugPaint = new Paint(); + debugPaint.setStrokeWidth(Utilities.dpToPx(1)); + for (int x = 0; x < mCountX; x++) { + for (int y = 0; y < mCountY; y++) { + if (!mOccupied.cells[x][y]) { + continue; } + targetCell[0] = x; + targetCell[1] = y; + + boolean canCreateFolder = canCreateFolder(getChildAt(x, y)); + cellToRect(x, y, 1, 1, cellBounds); + cellBoundsWithSpacing.set(cellBounds); + cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); + getWorkspaceCellVisualCenter(x, y, cellCenter); + + canvas.save(); + canvas.clipRect(cellBoundsWithSpacing); + + // Draw reorder drag target. + debugPaint.setColor(Color.RED); + canvas.drawCircle(cellCenter[0], cellCenter[1], getReorderRadius(targetCell), + debugPaint); + + // Draw folder creation drag target. + if (canCreateFolder) { + debugPaint.setColor(Color.GREEN); + canvas.drawCircle(cellCenter[0], cellCenter[1], + getFolderCreationRadius(targetCell), debugPaint); + } + + canvas.restore(); } } } @@ -481,6 +508,14 @@ public class CellLayout extends ViewGroup { } } + /** + * Returns whether dropping an icon on the given View can create (or add to) a folder. + */ + private boolean canCreateFolder(View child) { + return child instanceof DraggableView + && ((DraggableView) child).getViewType() == DRAGGABLE_ICON; + } + /** * Indicates the progress of the Workspace entering the SpringLoaded state; allows the * CellLayout to update various visuals for this state. @@ -817,7 +852,7 @@ public class CellLayout extends ViewGroup { } /** - * Given a cell coordinate and span return the point that represents the center of the regio + * Given a cell coordinate and span return the point that represents the center of the region * * @param cellX X coordinate of the cell * @param cellY Y coordinate of the cell @@ -830,11 +865,65 @@ public class CellLayout extends ViewGroup { result[1] = mTempRect.centerY(); } - public float getDistanceFromCell(float x, float y, int[] cell) { - cellToCenterPoint(cell[0], cell[1], mTmpPoint); + /** + * Returns the distance between the given coordinate and the visual center of the given cell. + */ + public float getDistanceFromWorkspaceCellVisualCenter(float x, float y, int[] cell) { + getWorkspaceCellVisualCenter(cell[0], cell[1], mTmpPoint); return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]); } + private void getWorkspaceCellVisualCenter(int cellX, int cellY, int[] outPoint) { + View child = getChildAt(cellX, cellY); + if (child instanceof DraggableView) { + DraggableView draggableChild = (DraggableView) child; + if (draggableChild.getViewType() == DRAGGABLE_ICON) { + cellToPoint(cellX, cellY, outPoint); + draggableChild.getWorkspaceVisualDragBounds(mTempRect); + mTempRect.offset(outPoint[0], outPoint[1]); + outPoint[0] = mTempRect.centerX(); + outPoint[1] = mTempRect.centerY(); + return; + } + } + cellToCenterPoint(cellX, cellY, outPoint); + } + + /** + * Returns the max distance from the center of a cell that can accept a drop to create a folder. + */ + public float getFolderCreationRadius(int[] targetCell) { + DeviceProfile grid = mActivity.getDeviceProfile(); + float iconVisibleRadius = ICON_VISIBLE_AREA_FACTOR * grid.iconSizePx / 2; + // Halfway between reorder radius and icon. + return (getReorderRadius(targetCell) + iconVisibleRadius) / 2; + } + + /** + * Returns the max distance from the center of a cell that will start to reorder on drag over. + */ + public float getReorderRadius(int[] targetCell) { + int[] centerPoint = mTmpPoint; + getWorkspaceCellVisualCenter(targetCell[0], targetCell[1], centerPoint); + + Rect cellBoundsWithSpacing = mTempRect; + cellToRect(targetCell[0], targetCell[1], 1, 1, cellBoundsWithSpacing); + cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); + + if (canCreateFolder(getChildAt(targetCell[0], targetCell[1]))) { + // Take only the circle in the smaller dimension, to ensure we don't start reordering + // too soon before accepting a folder drop. + int minRadius = centerPoint[0] - cellBoundsWithSpacing.left; + minRadius = Math.min(minRadius, centerPoint[1] - cellBoundsWithSpacing.top); + minRadius = Math.min(minRadius, cellBoundsWithSpacing.right - centerPoint[0]); + minRadius = Math.min(minRadius, cellBoundsWithSpacing.bottom - centerPoint[1]); + return minRadius; + } + // Take up the entire cell, including space between this cell and the adjacent ones. + return (float) Math.hypot(cellBoundsWithSpacing.width() / 2f, + cellBoundsWithSpacing.height() / 2f); + } + public int getCellWidth() { return mCellWidth; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 131fbfb071..203df0a2e1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -220,7 +220,6 @@ public class Workspace extends PagedView private FolderIcon mDragOverFolderIcon = null; private boolean mCreateUserFolderOnDrop = false; private boolean mAddToExistingFolderOnDrop = false; - private float mMaxDistanceForFolderCreation; // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget) private float mXDown; @@ -308,8 +307,6 @@ public class Workspace extends PagedView public void setInsets(Rect insets) { DeviceProfile grid = mLauncher.getDeviceProfile(); - mMaxDistanceForFolderCreation = grid.isTablet - ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); Rect padding = grid.workspacePadding; @@ -1774,8 +1771,8 @@ public class Workspace extends PagedView mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout, mTargetCell); - float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo, dropTargetLayout, mTargetCell, distance, true)) { return true; @@ -1809,7 +1806,7 @@ public class Workspace extends PagedView boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float distance, boolean considerTimeout) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willCreateUserFolder(info, dropOverView, considerTimeout); } @@ -1844,7 +1841,7 @@ public class Workspace extends PagedView boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell, float distance) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willAddToExistingUserFolder(dragInfo, dropOverView); @@ -1868,7 +1865,7 @@ public class Workspace extends PagedView boolean createUserFolderIfNecessary(View newView, int container, CellLayout target, int[] targetCell, float distance, boolean external, DragObject d) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); boolean hasntMoved = false; @@ -1925,7 +1922,7 @@ public class Workspace extends PagedView boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell, float distance, DragObject d, boolean external) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (!mAddToExistingFolderOnDrop) return false; @@ -1989,8 +1986,8 @@ public class Workspace extends PagedView mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell); - float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. @@ -2418,7 +2415,7 @@ public class Workspace extends PagedView setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]); - float targetCellDistance = mDragTargetLayout.getDistanceFromCell( + float targetCellDistance = mDragTargetLayout.getDistanceFromWorkspaceCellVisualCenter( mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); manageFolderFeedback(targetCellDistance, d); @@ -2431,8 +2428,9 @@ public class Workspace extends PagedView mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, d); } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) - && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX || - mLastReorderY != reorderY)) { + && !mReorderAlarm.alarmPending() + && (mLastReorderX != reorderX || mLastReorderY != reorderY) + && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) { int[] resultSpan = new int[2]; mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], @@ -2529,7 +2527,7 @@ public class Workspace extends PagedView } private void manageFolderFeedback(float distance, DragObject dragObject) { - if (distance > mMaxDistanceForFolderCreation) { + if (distance > mDragTargetLayout.getFolderCreationRadius(mTargetCell)) { if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER || mDragMode == DRAG_MODE_CREATE_FOLDER)) { setDragMode(DRAG_MODE_NONE); @@ -2674,8 +2672,8 @@ public class Workspace extends PagedView if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); - float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true) || willAddToExistingUserFolder( d.dragInfo, cellLayout, mTargetCell, distance)) { @@ -2774,8 +2772,8 @@ public class Workspace extends PagedView if (touchXY != null) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); - float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance, true, d)) { return; diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index c4d81660e0..98cb5ae5b5 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -72,6 +72,7 @@ public final class FeatureFlags { "PROMISE_APPS_NEW_INSTALLS", true, "Adds a promise icon to the home screen for new install sessions."); + // TODO: b/206508141: Long pressing on some icons on home screen cause launcher to crash. public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag( "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups."); diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index 5a1e4bf27e..b1a41093f5 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -77,44 +77,45 @@ public abstract class ArrowPopup extends AbstractFloatingView { // Duration values (ms) for popup open and close animations. - private static final int OPEN_DURATION = 276; - private static final int OPEN_FADE_START_DELAY = 0; - private static final int OPEN_FADE_DURATION = 38; - private static final int OPEN_CHILD_FADE_START_DELAY = 38; - private static final int OPEN_CHILD_FADE_DURATION = 76; + protected int OPEN_DURATION = 276; + protected int OPEN_FADE_START_DELAY = 0; + protected int OPEN_FADE_DURATION = 38; + protected int OPEN_CHILD_FADE_START_DELAY = 38; + protected int OPEN_CHILD_FADE_DURATION = 76; - private static final int CLOSE_DURATION = 200; - private static final int CLOSE_FADE_START_DELAY = 140; - private static final int CLOSE_FADE_DURATION = 50; - private static final int CLOSE_CHILD_FADE_START_DELAY = 0; - private static final int CLOSE_CHILD_FADE_DURATION = 140; + protected int CLOSE_DURATION = 200; + protected int CLOSE_FADE_START_DELAY = 140; + protected int CLOSE_FADE_DURATION = 50; + protected int CLOSE_CHILD_FADE_START_DELAY = 0; + protected int CLOSE_CHILD_FADE_DURATION = 140; // Index used to get background color when using local wallpaper color extraction, private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800; private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50; - private final Rect mTempRect = new Rect(); + protected final Rect mTempRect = new Rect(); protected final LayoutInflater mInflater; - private final float mOutlineRadius; + protected final float mOutlineRadius; protected final T mActivityContext; protected final boolean mIsRtl; - private final int mArrowOffsetVertical; - private final int mArrowOffsetHorizontal; - private final int mArrowWidth; - private final int mArrowHeight; - private final int mArrowPointRadius; - private final View mArrow; + protected final int mArrowOffsetVertical; + protected final int mArrowOffsetHorizontal; + protected final int mArrowWidth; + protected final int mArrowHeight; + protected final int mArrowPointRadius; + protected final View mArrow; private final int mMargin; protected boolean mIsLeftAligned; protected boolean mIsAboveIcon; - private int mGravity; + protected int mGravity; protected AnimatorSet mOpenCloseAnimator; protected boolean mDeferContainerRemoval; + protected boolean shouldScaleArrow = false; private final GradientDrawable mRoundedTop; private final GradientDrawable mRoundedBottom; @@ -122,10 +123,10 @@ public abstract class ArrowPopup private Runnable mOnCloseCallback = () -> { }; // The rect string of the view that the arrow is attached to, in screen reference frame. - private int mArrowColor; + protected int mArrowColor; protected final List mColorExtractors; - private final float mElevation; + protected final float mElevation; private final int mBackgroundColor; private final String mIterateChildrenTag; @@ -729,6 +730,14 @@ public abstract class ArrowPopup scale.setInterpolator(interpolator); animatorSet.play(scale); + if (shouldScaleArrow) { + Animator arrowScaleAnimator = ObjectAnimator.ofFloat(mArrow, View.SCALE_Y, + scaleValues); + arrowScaleAnimator.setDuration(totalDuration); + arrowScaleAnimator.setInterpolator(interpolator); + animatorSet.play(arrowScaleAnimator); + } + fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet); return animatorSet; diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java index e662d5c039..436aa51de9 100644 --- a/src/com/android/launcher3/popup/RoundedArrowDrawable.java +++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java @@ -78,6 +78,32 @@ public class RoundedArrowDrawable extends Drawable { mPath.transform(pathTransform); } + /** + * Constructor for an arrow that points to the left or right. + * + * @param width of the arrow. + * @param height of the arrow. + * @param radius of the tip of the arrow. + * @param isPointingLeft or not. + * @param color to draw the triangle. + */ + public RoundedArrowDrawable(float width, float height, float radius, boolean isPointingLeft, + int color) { + mPath = new Path(); + mPaint = new Paint(); + mPaint.setColor(color); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setAntiAlias(true); + + // Make the drawable with the triangle pointing down... + addDownPointingRoundedTriangleToPath(width, height, radius, mPath); + + // ... then rotate it to the side it needs to point. + Matrix pathTransform = new Matrix(); + pathTransform.setRotate(isPointingLeft ? 90 : -90, width * 0.5f, height * 0.5f); + mPath.transform(pathTransform); + } + @Override public void draw(Canvas canvas) { canvas.drawPath(mPath, mPaint); diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 93e3ea7599..498f6dbc60 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -429,7 +429,7 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, - int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds, + int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); @@ -439,13 +439,12 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { splitConfig.visualDividerBounds.height() : splitConfig.visualDividerBounds.width()); - int primaryHeight = primarySnapshotBounds.height(); primaryIconParams.gravity = (isRtl ? START : END) | TOP; - primaryIconView.setTranslationY(primaryHeight - primaryIconView.getHeight() / 2f); + primaryIconView.setTranslationY(primarySnapshotHeight - primaryIconView.getHeight() / 2f); primaryIconView.setTranslationX(0); secondaryIconParams.gravity = (isRtl ? START : END) | TOP; - secondaryIconView.setTranslationY(primaryHeight + taskIconHeight + dividerBar); + secondaryIconView.setTranslationY(primarySnapshotHeight + taskIconHeight + dividerBar); secondaryIconView.setTranslationX(0); primaryIconView.setLayoutParams(primaryIconParams); secondaryIconView.setLayoutParams(secondaryIconParams); diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index 2ff2feb4c5..95336cd399 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -152,7 +152,7 @@ public interface PagedOrientationHandler { void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight, FrameLayout.LayoutParams snapshotParams, boolean isRtl); void setSplitIconParams(View primaryIconView, View secondaryIconView, - int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds, + int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig); /* diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index ba9d09cfc6..835c2401ae 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -523,7 +523,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, - int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds, + int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); @@ -533,15 +533,12 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { splitConfig.visualDividerBounds.height() : splitConfig.visualDividerBounds.width()); - int primaryWidth = primarySnapshotBounds.width(); if (deviceProfile.isLandscape) { primaryIconParams.gravity = TOP | START; - primaryIconView.setTranslationX(primaryWidth - primaryIconView.getWidth()); + primaryIconView.setTranslationX(primarySnapshotWidth - primaryIconView.getWidth()); primaryIconView.setTranslationY(0); - secondaryIconParams.gravity = TOP | START; - secondaryIconView.setTranslationX(primaryWidth + dividerBar); - secondaryIconView.setTranslationY(0); + secondaryIconView.setTranslationX(primarySnapshotWidth + dividerBar); } else { primaryIconParams.gravity = TOP | CENTER_HORIZONTAL; primaryIconView.setTranslationX(-(primaryIconView.getWidth()) / 2f); @@ -549,8 +546,8 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL; secondaryIconView.setTranslationX(secondaryIconView.getWidth() / 2f); - secondaryIconView.setTranslationY(0); } + secondaryIconView.setTranslationY(0); primaryIconView.setLayoutParams(primaryIconParams); secondaryIconView.setLayoutParams(secondaryIconParams); } diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java index a0dde22d84..539e3f82c5 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -132,10 +132,10 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, - int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds, + int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight, - primarySnapshotBounds, secondarySnapshotBounds, isRtl, deviceProfile, splitConfig); + primarySnapshotWidth, primarySnapshotHeight, isRtl, deviceProfile, splitConfig); FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java index aacb9c5dff..784f4f050e 100644 --- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java +++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java @@ -25,11 +25,15 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Handler; import android.os.Process; +import android.os.UserHandle; +import android.util.ArrayMap; import android.util.Log; import android.util.Size; @@ -40,6 +44,7 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.BitmapRenderer; +import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.ShadowGenerator; import com.android.launcher3.icons.cache.HandlerRunnable; @@ -60,6 +65,9 @@ public class DatabaseWidgetPreviewLoader { private final Context mContext; private final float mPreviewBoxCornerRadius; + private final UserHandle mMyUser = Process.myUserHandle(); + private final ArrayMap mUserBadges = new ArrayMap<>(); + public DatabaseWidgetPreviewLoader(Context context) { mContext = context; float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context); @@ -100,6 +108,52 @@ public class DatabaseWidgetPreviewLoader { } } + /** + * Returns a drawable that can be used as a badge for the user or null. + */ + // @UiThread + public Drawable getBadgeForUser(UserHandle user, int badgeSize) { + if (mMyUser.equals(user)) { + return null; + } + + Bitmap badgeBitmap = getUserBadge(user, badgeSize); + FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap); + d.setFilterBitmap(true); + d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight()); + return d; + } + + private Bitmap getUserBadge(UserHandle user, int badgeSize) { + synchronized (mUserBadges) { + Bitmap badgeBitmap = mUserBadges.get(user); + if (badgeBitmap != null) { + return badgeBitmap; + } + + final Resources res = mContext.getResources(); + badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888); + + Drawable drawable = mContext.getPackageManager().getUserBadgedDrawableForDensity( + new BitmapDrawable(res, badgeBitmap), user, + new Rect(0, 0, badgeSize, badgeSize), + 0); + if (drawable instanceof BitmapDrawable) { + badgeBitmap = ((BitmapDrawable) drawable).getBitmap(); + } else { + badgeBitmap.eraseColor(Color.TRANSPARENT); + Canvas c = new Canvas(badgeBitmap); + drawable.setBounds(0, 0, badgeSize, badgeSize); + drawable.draw(c); + c.setBitmap(null); + } + + mUserBadges.put(user, badgeBitmap); + return badgeBitmap; + } + } + + /** * Generates the widget preview from either the {@link WidgetManagerHelper} or cache * and add badge at the bottom right corner. diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index f1ac656d60..c92fe5a516 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.TextView; @@ -47,6 +48,7 @@ import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.icons.BaseIconFactory; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.RoundDrawableWrapper; import com.android.launcher3.icons.cache.HandlerRunnable; @@ -111,6 +113,7 @@ public class WidgetCell extends LinearLayout { private FrameLayout mWidgetImageContainer; private WidgetImageView mWidgetImage; + private ImageView mWidgetBadge; private TextView mWidgetName; private TextView mWidgetDims; private TextView mWidgetDescription; @@ -166,6 +169,7 @@ public class WidgetCell extends LinearLayout { mWidgetImageContainer = findViewById(R.id.widget_preview_container); mWidgetImage = findViewById(R.id.widget_preview); + mWidgetBadge = findViewById(R.id.widget_badge); mWidgetName = findViewById(R.id.widget_name); mWidgetDims = findViewById(R.id.widget_dims); mWidgetDescription = findViewById(R.id.widget_description); @@ -195,6 +199,8 @@ public class WidgetCell extends LinearLayout { mWidgetImage.animate().cancel(); mWidgetImage.setDrawable(null); mWidgetImage.setVisibility(View.VISIBLE); + mWidgetBadge.setImageDrawable(null); + mWidgetBadge.setVisibility(View.GONE); mWidgetName.setText(null); mWidgetDims.setText(null); mWidgetDescription.setText(null); @@ -349,6 +355,7 @@ public class WidgetCell extends LinearLayout { mAppWidgetHostViewPreview = null; } } + if (mAnimatePreview) { mWidgetImageContainer.setAlpha(0f); ViewPropertyAnimator anim = mWidgetImageContainer.animate(); @@ -362,6 +369,20 @@ public class WidgetCell extends LinearLayout { } } + /** Used to show the badge when the widget is in the recommended section + */ + public void showBadge() { + Drawable badge = mWidgetPreviewLoader.getBadgeForUser(mItem.user, + BaseIconFactory.getBadgeSizeForIconSize( + mActivity.getDeviceProfile().allAppsIconSizePx)); + if (badge == null) { + mWidgetBadge.setVisibility(View.GONE); + } else { + mWidgetBadge.setVisibility(View.VISIBLE); + mWidgetBadge.setImageDrawable(badge); + } + } + private void setContainerSize(int width, int height) { LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams(); layoutParams.width = width; diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java index c986007a3f..06cc65e4c1 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java @@ -109,6 +109,7 @@ public final class WidgetsRecommendationTableLayout extends TableLayout { for (WidgetItem widgetItem : widgetItems) { WidgetCell widgetCell = addItemCell(tableRow); widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale); + widgetCell.showBadge(); } addView(tableRow); } diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java index de36d5f1b5..f33a50ae5a 100644 --- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java +++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java @@ -41,7 +41,7 @@ public class TestStabilityRule implements TestRule { Pattern.compile("^(" + "(?(BuildFromAndroidStudio|" + "([0-9]+|[A-Z])-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+))|" - + "(?[A-Z]([a-z]|[0-9])*)" + + "(?([A-Z][a-z]*[0-9]*|[0-9]+)*)" + ")$"); private static final Pattern PLATFORM_BUILD = Pattern.compile("^("