diff --git a/Android.bp b/Android.bp index a523a62bcb..b3027bc440 100644 --- a/Android.bp +++ b/Android.bp @@ -107,7 +107,6 @@ android_library { "androidx.cardview_cardview", "com.google.android.material_material", "iconloader_base", - "modules-utils-build", ], manifest: "AndroidManifest-common.xml", sdk_version: "current", diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 3b02599ada..eda08239d6 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -53,6 +53,7 @@ import java.util.function.Consumer; public class DepthController implements StateHandler, BaseActivity.MultiWindowModeChangedListener { + private static final boolean OVERLAY_SCROLL_ENABLED = false; public static final FloatProperty DEPTH = new FloatProperty("depth") { @Override @@ -294,6 +295,9 @@ public class DepthController implements StateHandler, } public void onOverlayScrollChanged(float progress) { + if (!OVERLAY_SCROLL_ENABLED) { + return; + } // Add some padding to the progress, such we don't change the depth on the last frames of // the animation. It's possible that a user flinging the feed quickly would scroll // horizontally by accident, causing the device to enter client composition unnecessarily. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index a22398d7e3..89e54b8863 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -156,14 +156,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { updateIconSize(resources); mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size); - // Inflate views. - mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate( - R.layout.taskbar, null, false); - TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view); - TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim); - FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view); - StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle); - + // Get display and corners first, as views might use them in constructor. Display display = windowContext.getDisplay(); Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY ? windowContext.getApplicationContext() @@ -172,6 +165,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); + // Inflate views. + mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate( + R.layout.taskbar, null, false); + TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view); + TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim); + FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view); + StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle); + mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this); // Construct controllers. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt new file mode 100644 index 0000000000..1177bdb484 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import com.android.launcher3.R + +/** + * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. + */ +class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { + + val paint: Paint = Paint() + var backgroundHeight = context.deviceProfile.taskbarSize.toFloat() + + private val leftCornerRadius = context.leftCornerRadius.toFloat() + private val rightCornerRadius = context.rightCornerRadius.toFloat() + private val invertedLeftCornerPath: Path = Path() + private val invertedRightCornerPath: Path = Path() + + init { + paint.color = context.getColor(R.color.taskbar_background) + paint.flags = Paint.ANTI_ALIAS_FLAG + paint.style = Paint.Style.FILL + + // Create the paths for the inverted rounded corners above the taskbar. Start with a filled + // square, and then subtract out a circle from the appropriate corner. + val square = Path() + square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW) + val circle = Path() + circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW) + invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE) + square.reset() + square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW) + circle.reset() + circle.addCircle(0f, 0f, rightCornerRadius, Path.Direction.CW) + invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE) + } + + /** + * Draws the background with the given paint and height, on the provided canvas. + */ + fun draw(canvas: Canvas) { + canvas.save() + canvas.translate(0f, canvas.height - backgroundHeight) + + // Draw the background behind taskbar content. + canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint) + + // Draw the inverted rounded corners above the taskbar. + canvas.translate(0f, -leftCornerRadius) + canvas.drawPath(invertedLeftCornerPath, paint) + canvas.translate(0f, leftCornerRadius) + canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius) + canvas.drawPath(invertedRightCornerPath, paint) + + canvas.restore() + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 9ba4a65e23..5c10565dc1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -477,7 +477,6 @@ public class TaskbarDragController extends DragController im tx.setScale(dragSurface, scale, scale); tx.setAlpha(dragSurface, alpha); tx.apply(); - tx.close(); } }); mReturnAnimator.addListener(new AnimatorListenerAdapter() { @@ -498,6 +497,7 @@ public class TaskbarDragController extends DragController im } private void cleanUpSurface() { + tx.close(); maybeOnDragEnd(); // Synchronize removing the drag surface with the next draw after calling // maybeOnDragEnd() @@ -508,7 +508,6 @@ public class TaskbarDragController extends DragController im syncer.addToSync(syncId, viewRoot.getView()); syncer.addTransactionToSync(syncId, transaction); syncer.markSyncReady(syncId); - mReturnAnimator = null; } }); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index 4a806650a5..089c26d3f9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -20,8 +20,6 @@ import static android.view.KeyEvent.KEYCODE_BACK; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; @@ -31,7 +29,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.R; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.BaseDragLayer; @@ -44,13 +41,11 @@ import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInset */ public class TaskbarDragLayer extends BaseDragLayer { - private final Paint mTaskbarBackgroundPaint; - private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath; + private final TaskbarBackgroundRenderer mBackgroundRenderer; private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets; // Initialized in init. private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks; - private float mLeftCornerRadius, mRightCornerRadius; private float mTaskbarBackgroundOffset; @@ -70,35 +65,13 @@ public class TaskbarDragLayer extends BaseDragLayer { public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, 1 /* alphaChannelCount */); - mTaskbarBackgroundPaint = new Paint(); - mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background)); - mTaskbarBackgroundPaint.setAlpha(0); - mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG); - mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL); - - // Will be set in init(), but this ensures they are always non-null. - mInvertedLeftCornerPath = new Path(); - mInvertedRightCornerPath = new Path(); + mBackgroundRenderer = new TaskbarBackgroundRenderer(mActivity); + mBackgroundRenderer.getPaint().setAlpha(0); } public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) { mControllerCallbacks = callbacks; - // Create the paths for the inverted rounded corners above the taskbar. Start with a filled - // square, and then subtracting out a circle from the appropriate corner. - mLeftCornerRadius = mActivity.getLeftCornerRadius(); - mRightCornerRadius = mActivity.getRightCornerRadius(); - Path square = new Path(); - square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW); - Path circle = new Path(); - circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW); - mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE); - square.reset(); - square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW); - circle.reset(); - circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW); - mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE); - recreateControllers(); } @@ -151,20 +124,8 @@ public class TaskbarDragLayer extends BaseDragLayer { protected void dispatchDraw(Canvas canvas) { float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight() * (1f - mTaskbarBackgroundOffset); - canvas.save(); - canvas.translate(0, canvas.getHeight() - backgroundHeight); - - // Draw the background behind taskbar content. - canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint); - - // Draw the inverted rounded corners above the taskbar. - canvas.translate(0, -mLeftCornerRadius); - canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint); - canvas.translate(0, mLeftCornerRadius); - canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius); - canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint); - - canvas.restore(); + mBackgroundRenderer.setBackgroundHeight(backgroundHeight); + mBackgroundRenderer.draw(canvas); super.dispatchDraw(canvas); } @@ -173,7 +134,7 @@ public class TaskbarDragLayer extends BaseDragLayer { * @param alpha 0 is fully transparent, 1 is fully opaque. */ protected void setTaskbarBackgroundAlpha(float alpha) { - mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255)); + mBackgroundRenderer.getPaint().setAlpha((int) (alpha * 255)); invalidate(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java index 94a3307bc3..1d3757fca2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java @@ -17,22 +17,19 @@ package com.android.launcher3.taskbar; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; import android.util.AttributeSet; import android.view.View; +import com.android.launcher3.views.ActivityContext; + /** * View that handles scrimming the taskbar and the inverted corners it draws. The scrim is used * when bubbles is expanded. */ public class TaskbarScrimView extends View { - private final Paint mTaskbarScrimPaint; - private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath; + private final TaskbarBackgroundRenderer mRenderer; private boolean mShowScrim; - private float mLeftCornerRadius, mRightCornerRadius; - private float mBackgroundHeight; public TaskbarScrimView(Context context) { this(context, null); @@ -49,14 +46,9 @@ public class TaskbarScrimView extends View { public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - - mTaskbarScrimPaint = new Paint(); - mTaskbarScrimPaint.setColor(getResources().getColor(android.R.color.system_neutral1_1000)); - mTaskbarScrimPaint.setFlags(Paint.ANTI_ALIAS_FLAG); - mTaskbarScrimPaint.setStyle(Paint.Style.FILL); - - mInvertedLeftCornerPath = new Path(); - mInvertedRightCornerPath = new Path(); + mRenderer = new TaskbarBackgroundRenderer(ActivityContext.lookupContext(context)); + mRenderer.getPaint().setColor(getResources().getColor( + android.R.color.system_neutral1_1000)); } @Override @@ -64,31 +56,7 @@ public class TaskbarScrimView extends View { super.onDraw(canvas); if (mShowScrim) { - canvas.save(); - canvas.translate(0, canvas.getHeight() - mBackgroundHeight); - - // Scrim the taskbar itself. - canvas.drawRect(0, 0, canvas.getWidth(), mBackgroundHeight, mTaskbarScrimPaint); - - // Scrim the inverted rounded corners above the taskbar. - canvas.translate(0, -mLeftCornerRadius); - canvas.drawPath(mInvertedLeftCornerPath, mTaskbarScrimPaint); - canvas.translate(0, mLeftCornerRadius); - canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius); - canvas.drawPath(mInvertedRightCornerPath, mTaskbarScrimPaint); - - canvas.restore(); - } - } - - /** - * Sets the height of the taskbar background. - * @param height the height of the background. - */ - protected void setBackgroundHeight(float height) { - mBackgroundHeight = height; - if (mShowScrim) { - invalidate(); + mRenderer.draw(canvas); } } @@ -98,32 +66,7 @@ public class TaskbarScrimView extends View { */ protected void setScrimAlpha(float alpha) { mShowScrim = alpha > 0f; - mTaskbarScrimPaint.setAlpha((int) (alpha * 255)); + mRenderer.getPaint().setAlpha((int) (alpha * 255)); invalidate(); } - - /** - * Sets the radius of the left and right corners above the taskbar. - * @param leftCornerRadius the radius of the left corner. - * @param rightCornerRadius the radius of the right corner. - */ - protected void setCornerSizes(float leftCornerRadius, float rightCornerRadius) { - mLeftCornerRadius = leftCornerRadius; - mRightCornerRadius = rightCornerRadius; - - Path square = new Path(); - square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW); - Path circle = new Path(); - circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW); - mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE); - square.reset(); - square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW); - circle.reset(); - circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW); - mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE); - - if (mShowScrim) { - invalidate(); - } - } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java index 02bbae4d53..58ace17787 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java @@ -49,9 +49,6 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa public TaskbarScrimViewController(TaskbarActivityContext activity, TaskbarScrimView scrimView) { mActivity = activity; mScrimView = scrimView; - mScrimView.setCornerSizes(mActivity.getLeftCornerRadius(), - mActivity.getRightCornerRadius()); - mScrimView.setBackgroundHeight(mActivity.getDeviceProfile().taskbarSize); } /** diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index ed00b8ec7b..e5cbbee39a 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -106,6 +106,7 @@ import android.view.animation.Interpolator; import android.widget.ListView; import android.widget.OverScroller; import android.widget.Toast; +import android.window.PictureInPictureSurfaceTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -161,6 +162,7 @@ import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskViewUtils; import com.android.quickstep.ViewUtils; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitScreenBounds; @@ -4507,6 +4509,18 @@ public abstract class RecentsView { if (onFinishComplete != null) { diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 6d39857ae1..8358f2a817 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -53,6 +53,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.DeadObjectException; import android.os.Handler; import android.os.Message; @@ -72,6 +73,7 @@ import android.view.ViewConfiguration; import android.view.animation.Interpolator; import android.widget.LinearLayout; +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.core.graphics.ColorUtils; @@ -93,7 +95,6 @@ import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.widget.PendingAddShortcutInfo; -import com.android.modules.utils.build.SdkLevel; import java.lang.reflect.Method; import java.util.ArrayList; @@ -122,15 +123,20 @@ public final class Utilities { public static final String[] EMPTY_STRING_ARRAY = new String[0]; public static final Person[] EMPTY_PERSON_ARRAY = new Person[0]; + @ChecksSdkIntAtLeast(api = VERSION_CODES.P) public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + @ChecksSdkIntAtLeast(api = VERSION_CODES.Q) public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; - public static final boolean ATLEAST_R = SdkLevel.isAtLeastR(); + @ChecksSdkIntAtLeast(api = VERSION_CODES.R) + public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; - public static final boolean ATLEAST_S = SdkLevel.isAtLeastS(); + @ChecksSdkIntAtLeast(api = VERSION_CODES.S) + public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; - public static final boolean ATLEAST_T = SdkLevel.isAtLeastT(); + @ChecksSdkIntAtLeast(api = VERSION_CODES.TIRAMISU, codename = "T") + public static final boolean ATLEAST_T = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; /** * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}. diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java index 74d9a228e2..6f295e6c50 100644 --- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java +++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java @@ -20,9 +20,13 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.annotation.TargetApi; import android.graphics.Bitmap; -import android.graphics.Matrix; +import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.Path.Direction; +import android.graphics.Picture; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; @@ -31,10 +35,11 @@ import android.os.Build; import android.util.Log; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import com.android.launcher3.Utilities; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; -import com.android.launcher3.graphics.ShiftedBitmapDrawable; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.util.Preconditions; import com.android.launcher3.views.ActivityContext; @@ -69,79 +74,104 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable { return mBadge; } + @TargetApi(Build.VERSION_CODES.P) public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon( - ActivityContext activity, int folderId, Point dragViewSize) { + ActivityContext activity, int folderId, Point size) { Preconditions.assertNonUiThread(); + if (!Utilities.ATLEAST_P) { + return null; + } - // Create the actual drawable on the UI thread to avoid race conditions with + // assume square + if (size.x != size.y) { + return null; + } + int requestedSize = size.x; + + // Only use the size actually needed for drawing the folder icon + int drawingSize = activity.getDeviceProfile().folderIconSizePx; + int foregroundSize = Math.max(requestedSize, drawingSize); + float shift = foregroundSize - requestedSize; + + Picture background = new Picture(); + Picture foreground = new Picture(); + Picture badge = new Picture(); + + Canvas bgCanvas = background.beginRecording(requestedSize, requestedSize); + Canvas badgeCanvas = badge.beginRecording(requestedSize, requestedSize); + + Canvas fgCanvas = foreground.beginRecording(foregroundSize, foregroundSize); + fgCanvas.translate(shift, shift); + + // Do not clip the folder drawing since the icon previews extend outside the background. + Path mask = new Path(); + mask.addRect(-shift, -shift, requestedSize + shift, requestedSize + shift, + Direction.CCW); + + // Initialize the actual draw commands on the UI thread to avoid race conditions with // FolderIcon draw pass try { - return MAIN_EXECUTOR.submit(() -> { + MAIN_EXECUTOR.submit(() -> { FolderIcon icon = activity.findFolderIcon(folderId); - return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize); - + if (icon == null) { + throw new IllegalArgumentException("Folder not found with id: " + folderId); + } + initLayersOnUiThread(icon, requestedSize, bgCanvas, fgCanvas, badgeCanvas); }).get(); } catch (Exception e) { Log.e(TAG, "Unable to create folder icon", e); return null; + } finally { + background.endRecording(); + foreground.endRecording(); + badge.endRecording(); } + + // Only convert foreground to a bitmap as it can contain multiple draw commands. Other + // layers either draw a nothing or a single draw call. + Bitmap fgBitmap = Bitmap.createBitmap(foreground); + Paint foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + // Do not use PictureDrawable as it moves the picture to the canvas bounds, whereas we want + // to draw it at (0,0) + return new FolderAdaptiveIcon( + new BitmapRendererDrawable(c -> c.drawPicture(background)), + new BitmapRendererDrawable( + c -> c.drawBitmap(fgBitmap, -shift, -shift, foregroundPaint)), + new BitmapRendererDrawable(c -> c.drawPicture(badge)), + mask); } - private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon, - Point dragViewSize) { - Preconditions.assertUIThread(); - + @UiThread + private static void initLayersOnUiThread(FolderIcon icon, int size, + Canvas backgroundCanvas, Canvas foregroundCanvas, Canvas badgeCanvas) { icon.getPreviewBounds(sTmpRect); - - PreviewBackground bg = icon.getFolderBackground(); - - // assume square - assert (dragViewSize.x == dragViewSize.y); final int previewSize = sTmpRect.width(); - final int margin = (dragViewSize.x - previewSize) / 2; + PreviewBackground bg = icon.getFolderBackground(); + final int margin = (size - previewSize) / 2; final float previewShiftX = -sTmpRect.left + margin; final float previewShiftY = -sTmpRect.top + margin; // Initialize badge, which consists of the outline stroke, shadow and dot; these // must be rendered above the foreground - Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, - (canvas) -> { - canvas.save(); - canvas.translate(previewShiftX, previewShiftY); - bg.drawShadow(canvas); - bg.drawBackgroundStroke(canvas); - icon.drawDot(canvas); - canvas.restore(); - }); + badgeCanvas.save(); + badgeCanvas.translate(previewShiftX, previewShiftY); + icon.drawDot(badgeCanvas); + badgeCanvas.restore(); - // Initialize mask - Path mask = new Path(); - Matrix m = new Matrix(); - m.setTranslate(previewShiftX, previewShiftY); - bg.getClipPath().transform(m, mask); + // Draw foreground + foregroundCanvas.save(); + foregroundCanvas.translate(previewShiftX, previewShiftY); + icon.getPreviewItemManager().draw(foregroundCanvas); + foregroundCanvas.restore(); - Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, - (canvas) -> { - canvas.save(); - canvas.translate(previewShiftX, previewShiftY); - icon.getPreviewItemManager().draw(canvas); - canvas.restore(); - }); - - Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, - (canvas) -> { - Paint p = new Paint(); - p.setColor(bg.getBgColor()); - - canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p); - }); - - ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0); - ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0); - ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0); - - return new FolderAdaptiveIcon(background, foreground, badge, mask); + // Draw background + Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + backgroundPaint.setColor(bg.getBgColor()); + bg.drawShadow(backgroundCanvas); + backgroundCanvas.drawCircle(size / 2f, size / 2f, bg.getRadius(), backgroundPaint); + bg.drawBackgroundStroke(backgroundCanvas); } @Override @@ -174,4 +204,52 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable { & mBadge.getChangingConfigurations(); } } + + private static class BitmapRendererDrawable extends Drawable { + + private final BitmapRenderer mRenderer; + + BitmapRendererDrawable(BitmapRenderer renderer) { + mRenderer = renderer; + } + + @Override + public void draw(Canvas canvas) { + mRenderer.draw(canvas); + } + + @Override + public void setAlpha(int i) { } + + @Override + public void setColorFilter(ColorFilter colorFilter) { } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public ConstantState getConstantState() { + return new MyConstantState(mRenderer); + } + + private static class MyConstantState extends ConstantState { + private final BitmapRenderer mRenderer; + + MyConstantState(BitmapRenderer renderer) { + mRenderer = renderer; + } + + @Override + public Drawable newDrawable() { + return new BitmapRendererDrawable(mRenderer); + } + + @Override + public int getChangingConfigurations() { + return 0; + } + } + } } diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java deleted file mode 100644 index f8583b89e0..0000000000 --- a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.graphics; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.drawable.Drawable; - -/** - * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds - */ -public class ShiftedBitmapDrawable extends Drawable { - - private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); - private final Bitmap mBitmap; - private float mShiftX; - private float mShiftY; - - private final ConstantState mConstantState; - - public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) { - mBitmap = bitmap; - mShiftX = shiftX; - mShiftY = shiftY; - - mConstantState = new MyConstantState(mBitmap, mShiftX, mShiftY); - } - - public float getShiftX() { - return mShiftX; - } - - public float getShiftY() { - return mShiftY; - } - - public void setShiftX(float shiftX) { - mShiftX = shiftX; - } - - public void setShiftY(float shiftY) { - mShiftY = shiftY; - } - - @Override - public void draw(Canvas canvas) { - canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint); - } - - @Override - public void setAlpha(int i) { } - - @Override - public void setColorFilter(ColorFilter colorFilter) { - mPaint.setColorFilter(colorFilter); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public ConstantState getConstantState() { - return mConstantState; - } - - private static class MyConstantState extends ConstantState { - private final Bitmap mBitmap; - private float mShiftX; - private float mShiftY; - - MyConstantState(Bitmap bitmap, float shiftX, float shiftY) { - mBitmap = bitmap; - mShiftX = shiftX; - mShiftY = shiftY; - } - - @Override - public Drawable newDrawable() { - return new ShiftedBitmapDrawable(mBitmap, mShiftX, mShiftY); - } - - @Override - public int getChangingConfigurations() { - return 0; - } - } -} \ No newline at end of file diff --git a/src/com/android/launcher3/model/StringCache.java b/src/com/android/launcher3/model/StringCache.java index 2fc852dcce..663a46327f 100644 --- a/src/com/android/launcher3/model/StringCache.java +++ b/src/com/android/launcher3/model/StringCache.java @@ -16,6 +16,7 @@ package com.android.launcher3.model; +import android.annotation.SuppressLint; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.Build; @@ -215,6 +216,7 @@ public class StringCache { context, DISABLED_BY_ADMIN_MESSAGE, R.string.msg_disabled_by_admin); } + @SuppressLint("NewApi") private String getEnterpriseString( Context context, String updatableStringId, int defaultStringId) { return Utilities.ATLEAST_T diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 56a1d3743f..babe607ca1 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -49,7 +49,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.icons.FastBitmapDrawable; @@ -413,8 +412,7 @@ public class FloatingIconView extends FrameLayout implements @WorkerThread @SuppressWarnings("WrongThread") private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) { - if (!(drawable instanceof AdaptiveIconDrawable) - || (drawable instanceof FolderAdaptiveIcon)) { + if (!(drawable instanceof AdaptiveIconDrawable)) { return 0; } int blurSizeOutline =