diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 90b55360ef..4b2e487da9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -118,7 +118,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe final RectF iconLocation = new RectF(); boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow(); FloatingIconView floatingIconView = canUseWorkspaceView - ? recentsView.getFloatingIconView(activity, workspaceView, iconLocation) + ? FloatingIconView.getFloatingIconView(activity, workspaceView, + true /* hideOriginal */, iconLocation, false /* isOpening */) : null; return new HomeAnimationFactory() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 661468ac62..1e1007e686 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -66,7 +66,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -77,7 +76,6 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; @@ -95,7 +93,6 @@ import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.PendingAnimation; import com.android.launcher3.util.Themes; import com.android.launcher3.util.ViewPool; -import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.RecentsAnimationWrapper; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener; @@ -308,8 +305,6 @@ public abstract class RecentsView extends PagedView impl private Layout mEmptyTextLayout; private LiveTileOverlay mLiveTileOverlay; - private FloatingIconView mFloatingIconView; - private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { if (!inMultiWindowMode && mOverviewStateEnabled) { @@ -1687,12 +1682,6 @@ public abstract class RecentsView extends PagedView impl } } - public FloatingIconView getFloatingIconView(Launcher launcher, View view, RectF iconLocation) { - mFloatingIconView = FloatingIconView.getFloatingIconView(launcher, view, - true /* hideOriginal */, iconLocation, false /* isOpening */, mFloatingIconView); - return mFloatingIconView; - } - public ClipAnimationHelper getTempClipAnimationHelper() { return mTempClipAnimationHelper; } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 91c460148d..95ae312891 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -142,7 +142,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private final float mClosingWindowTransY; private DeviceProfile mDeviceProfile; - private FloatingIconView mFloatingView; private RemoteAnimationProvider mRemoteAnimationProvider; @@ -411,15 +410,15 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets, Rect windowTargetBounds, boolean toggleVisibility) { RectF bounds = new RectF(); - mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility, - bounds, true /* isOpening */, mFloatingView); + FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v, + toggleVisibility, bounds, true /* isOpening */); Rect crop = new Rect(); Matrix matrix = new Matrix(); RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets, MODE_OPENING); SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mFloatingView); + new SyncRtSurfaceTransactionApplierCompat(floatingView); openingTargets.addDependentTransactionApplier(surfaceApplier); // Scale the app icon to take up the entire screen. This simplifies the math when @@ -463,7 +462,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setDuration(APP_LAUNCH_DURATION); appAnimator.setInterpolator(LINEAR); - appAnimator.addListener(mFloatingView); + appAnimator.addListener(floatingView); appAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -557,7 +556,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans } else { currentBounds.bottom -= croppedHeight; } - mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f, + floatingView.update(currentBounds, mIconAlpha.value, percent, 0f, cornerRadius * scale, true /* isOpening */); } else { matrix.setTranslate(target.position.x, target.position.y); diff --git a/res/layout/floating_icon_view.xml b/res/layout/floating_icon_view.xml new file mode 100644 index 0000000000..240c486b19 --- /dev/null +++ b/res/layout/floating_icon_view.xml @@ -0,0 +1,19 @@ + + + diff --git a/res/values/config.xml b/res/values/config.xml index 83aea8b1f0..984729b20f 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -86,7 +86,6 @@ - diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 7f7224241b..424ffde1ff 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -36,6 +36,7 @@ import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.SystemUiController; +import com.android.launcher3.util.ViewCache; import com.android.launcher3.views.ActivityContext; import java.io.FileDescriptor; @@ -102,6 +103,12 @@ public abstract class BaseActivity extends Activity // animation @InvisibilityFlags private int mForceInvisible; + private final ViewCache mViewCache = new ViewCache(); + + public ViewCache getViewCache() { + return mViewCache; + } + @Override public DeviceProfile getDeviceProfile() { return mDeviceProfile; diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 7af12c57d4..9d46cf2abf 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -44,7 +44,6 @@ import android.view.View; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; @@ -55,6 +54,7 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.UiThreadHelper; import java.util.Arrays; @@ -210,8 +210,7 @@ public class DragView extends View implements LauncherStateManager.StateListener return; } // Load the adaptive icon on a background thread and add the view in ui thread. - final Looper workerLooper = LauncherModel.getWorkerLooper(); - new Handler(workerLooper).postAtFrontOfQueue(new Runnable() { + new Handler(UiThreadHelper.getBackgroundLooper()).postAtFrontOfQueue(new Runnable() { @Override public void run() { Object[] outObj = new Object[1]; diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 0a9bc722d1..250169cdb2 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -102,7 +102,6 @@ public class FolderIcon extends FrameLayout implements FolderListener { private List mCurrentPreviewItems = new ArrayList<>(); boolean mAnimating = false; - private Rect mTempBounds = new Rect(); private float mSlop; diff --git a/src/com/android/launcher3/util/ViewCache.java b/src/com/android/launcher3/util/ViewCache.java new file mode 100644 index 0000000000..08b8744167 --- /dev/null +++ b/src/com/android/launcher3/util/ViewCache.java @@ -0,0 +1,73 @@ +/* + * 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.util; + +import android.content.Context; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * Utility class to cache views at an activity level + */ +public class ViewCache { + + protected final SparseArray mCache = new SparseArray(); + + public void setCacheSize(int layoutId, int size) { + mCache.put(layoutId, new CacheEntry(size)); + } + + public T getView(int layoutId, Context context, ViewGroup parent) { + CacheEntry entry = mCache.get(layoutId); + if (entry == null) { + entry = new CacheEntry(1); + mCache.put(layoutId, entry); + } + + if (entry.mCurrentSize > 0) { + entry.mCurrentSize --; + T result = (T) entry.mViews[entry.mCurrentSize]; + entry.mViews[entry.mCurrentSize] = null; + return result; + } + + return (T) LayoutInflater.from(context).inflate(layoutId, parent, false); + } + + public void recycleView(int layoutId, View view) { + CacheEntry entry = mCache.get(layoutId); + if (entry != null && entry.mCurrentSize < entry.mMaxSize) { + entry.mViews[entry.mCurrentSize] = view; + entry.mCurrentSize++; + } + } + + private static class CacheEntry { + + final int mMaxSize; + final View[] mViews; + + int mCurrentSize; + + public CacheEntry(int maxSize) { + mMaxSize = maxSize; + mViews = new View[maxSize]; + mCurrentSize = 0; + } + } +} diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 3d5877a597..f63bcddf0d 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -27,6 +27,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; +import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Outline; @@ -40,6 +41,7 @@ import android.os.Build; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; +import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; @@ -61,6 +63,7 @@ import com.android.launcher3.graphics.ShiftedBitmapDrawable; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.util.UiThreadHelper; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -148,12 +151,20 @@ public class FloatingIconView extends View implements private final SpringAnimation mFgSpringX; private float mFgTransX; - private FloatingIconView(Launcher launcher) { - super(launcher); - mLauncher = launcher; + public FloatingIconView(Context context) { + this(context, null); + } + + public FloatingIconView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FloatingIconView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(context); mBlurSizeOutline = getResources().getDimensionPixelSize( R.dimen.blur_size_medium_outline); - mListenerView = new ListenerView(launcher, null); + mListenerView = new ListenerView(context, attrs); mFgSpringX = new SpringAnimation(this, mFgTransXProperty) .setSpring(new SpringForce() @@ -350,6 +361,7 @@ public class FloatingIconView extends View implements } @WorkerThread + @SuppressWarnings("WrongThread") private void getIcon(View v, ItemInfo info, boolean isOpening, Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) { final LayoutParams lp = (LayoutParams) getLayoutParams(); @@ -396,7 +408,7 @@ public class FloatingIconView extends View implements && finalDrawable instanceof AdaptiveIconDrawable; int iconOffset = getOffsetForIconBounds(finalDrawable); - new Handler(Looper.getMainLooper()).post(() -> { + mLauncher.getMainExecutor().execute(() -> { if (isAdaptiveIcon) { mIsAdaptiveIcon = true; boolean isFolderIcon = finalDrawable instanceof FolderAdaptiveIcon; @@ -505,6 +517,7 @@ public class FloatingIconView extends View implements } @WorkerThread + @SuppressWarnings("WrongThread") private int getOffsetForIconBounds(Drawable drawable) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !(drawable instanceof AdaptiveIconDrawable)) { @@ -515,7 +528,7 @@ public class FloatingIconView extends View implements Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline); bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2); - try (LauncherIcons li = LauncherIcons.obtain(getContext())) { + try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) { Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null)); } @@ -604,11 +617,14 @@ public class FloatingIconView extends View implements * @param isOpening True if this view replaces the icon for app open animation. */ public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView, - boolean hideOriginal, RectF positionOut, boolean isOpening, FloatingIconView recycle) { - if (recycle != null) { - recycle.recycle(); - } - FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher); + boolean hideOriginal, RectF positionOut, boolean isOpening) { + final DragLayer dragLayer = launcher.getDragLayer(); + ViewGroup parent = (ViewGroup) dragLayer.getParent(); + + FloatingIconView view = launcher.getViewCache().getView(R.layout.floating_icon_view, + launcher, parent); + view.recycle(); + view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout(); view.mOriginalIcon = originalView; @@ -626,16 +642,15 @@ public class FloatingIconView extends View implements originalView.setVisibility(INVISIBLE); }; CancellationSignal loadIconSignal = view.mLoadIconSignal; - new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { + new Handler(UiThreadHelper.getBackgroundLooper()).postAtFrontOfQueue(() -> { view.getIcon(originalView, (ItemInfo) originalView.getTag(), isOpening, onIconLoaded, loadIconSignal); }); } // We need to add it to the overlay, but keep it invisible until animation starts.. - final DragLayer dragLayer = launcher.getDragLayer(); view.setVisibility(INVISIBLE); - ((ViewGroup) dragLayer.getParent()).addView(view); + parent.addView(view); dragLayer.addView(view.mListenerView); view.mListenerView.setListener(view::onListenerViewClosed); @@ -714,6 +729,7 @@ public class FloatingIconView extends View implements ((ViewGroup) dragLayer.getParent()).removeView(this); dragLayer.removeView(mListenerView); recycle(); + mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this); } private void recycle() {