Adding a utility class to cache views at an activity level

Bug: 122345781
Change-Id: I9a939e0b19c06c1089c1ceb515f8b97fb5dbb49e
This commit is contained in:
Sunny Goyal 2019-05-22 14:13:53 -07:00
parent 07b985b5e5
commit 5686333117
10 changed files with 138 additions and 37 deletions

View File

@ -118,7 +118,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
final RectF iconLocation = new RectF(); final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow(); boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
FloatingIconView floatingIconView = canUseWorkspaceView FloatingIconView floatingIconView = canUseWorkspaceView
? recentsView.getFloatingIconView(activity, workspaceView, iconLocation) ? FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null; : null;
return new HomeAnimationFactory() { return new HomeAnimationFactory() {

View File

@ -66,7 +66,6 @@ import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug; import android.view.ViewDebug;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
@ -77,7 +76,6 @@ import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable; import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView; 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.PendingAnimation;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool; import com.android.launcher3.util.ViewPool;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RecentsAnimationWrapper; import com.android.quickstep.RecentsAnimationWrapper;
import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener; import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
@ -308,8 +305,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private Layout mEmptyTextLayout; private Layout mEmptyTextLayout;
private LiveTileOverlay mLiveTileOverlay; private LiveTileOverlay mLiveTileOverlay;
private FloatingIconView mFloatingIconView;
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> { (inMultiWindowMode) -> {
if (!inMultiWindowMode && mOverviewStateEnabled) { if (!inMultiWindowMode && mOverviewStateEnabled) {
@ -1687,12 +1682,6 @@ public abstract class RecentsView<T extends BaseActivity> 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() { public ClipAnimationHelper getTempClipAnimationHelper() {
return mTempClipAnimationHelper; return mTempClipAnimationHelper;
} }

View File

@ -142,7 +142,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
private final float mClosingWindowTransY; private final float mClosingWindowTransY;
private DeviceProfile mDeviceProfile; private DeviceProfile mDeviceProfile;
private FloatingIconView mFloatingView;
private RemoteAnimationProvider mRemoteAnimationProvider; private RemoteAnimationProvider mRemoteAnimationProvider;
@ -411,15 +410,15 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets, private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
Rect windowTargetBounds, boolean toggleVisibility) { Rect windowTargetBounds, boolean toggleVisibility) {
RectF bounds = new RectF(); RectF bounds = new RectF();
mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility, FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
bounds, true /* isOpening */, mFloatingView); toggleVisibility, bounds, true /* isOpening */);
Rect crop = new Rect(); Rect crop = new Rect();
Matrix matrix = new Matrix(); Matrix matrix = new Matrix();
RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets, RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
MODE_OPENING); MODE_OPENING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier = SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mFloatingView); new SyncRtSurfaceTransactionApplierCompat(floatingView);
openingTargets.addDependentTransactionApplier(surfaceApplier); openingTargets.addDependentTransactionApplier(surfaceApplier);
// Scale the app icon to take up the entire screen. This simplifies the math when // 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); ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION); appAnimator.setDuration(APP_LAUNCH_DURATION);
appAnimator.setInterpolator(LINEAR); appAnimator.setInterpolator(LINEAR);
appAnimator.addListener(mFloatingView); appAnimator.addListener(floatingView);
appAnimator.addListener(new AnimatorListenerAdapter() { appAnimator.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
@ -557,7 +556,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
} else { } else {
currentBounds.bottom -= croppedHeight; currentBounds.bottom -= croppedHeight;
} }
mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f, floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
cornerRadius * scale, true /* isOpening */); cornerRadius * scale, true /* isOpening */);
} else { } else {
matrix.setTranslate(target.position.x, target.position.y); matrix.setTranslate(target.position.x, target.position.y);

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.launcher3.views.FloatingIconView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

View File

@ -86,7 +86,6 @@
<!-- View IDs to store item highlight information --> <!-- View IDs to store item highlight information -->
<item type="id" name="view_unhighlight_background" /> <item type="id" name="view_unhighlight_background" />
<item type="id" name="view_highlighted" />
<!-- Menu id for feature flags --> <!-- Menu id for feature flags -->
<item type="id" name="menu_apply_flags" /> <item type="id" name="menu_apply_flags" />

View File

@ -36,6 +36,7 @@ import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.ActivityContext;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@ -102,6 +103,12 @@ public abstract class BaseActivity extends Activity
// animation // animation
@InvisibilityFlags private int mForceInvisible; @InvisibilityFlags private int mForceInvisible;
private final ViewCache mViewCache = new ViewCache();
public ViewCache getViewCache() {
return mViewCache;
}
@Override @Override
public DeviceProfile getDeviceProfile() { public DeviceProfile getDeviceProfile() {
return mDeviceProfile; return mDeviceProfile;

View File

@ -44,7 +44,6 @@ import android.view.View;
import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo; import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager; 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.icons.LauncherIcons;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.UiThreadHelper;
import java.util.Arrays; import java.util.Arrays;
@ -210,8 +210,7 @@ public class DragView extends View implements LauncherStateManager.StateListener
return; return;
} }
// Load the adaptive icon on a background thread and add the view in ui thread. // Load the adaptive icon on a background thread and add the view in ui thread.
final Looper workerLooper = LauncherModel.getWorkerLooper(); new Handler(UiThreadHelper.getBackgroundLooper()).postAtFrontOfQueue(new Runnable() {
new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
@Override @Override
public void run() { public void run() {
Object[] outObj = new Object[1]; Object[] outObj = new Object[1];

View File

@ -102,7 +102,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>(); private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
boolean mAnimating = false; boolean mAnimating = false;
private Rect mTempBounds = new Rect();
private float mSlop; private float mSlop;

View File

@ -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<CacheEntry> mCache = new SparseArray();
public void setCacheSize(int layoutId, int size) {
mCache.put(layoutId, new CacheEntry(size));
}
public <T extends View> 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;
}
}
}

View File

@ -27,6 +27,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Outline; import android.graphics.Outline;
@ -40,6 +41,7 @@ import android.os.Build;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewOutlineProvider; import android.view.ViewOutlineProvider;
@ -61,6 +63,7 @@ import com.android.launcher3.graphics.ShiftedBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.UiThreadHelper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
@ -148,12 +151,20 @@ public class FloatingIconView extends View implements
private final SpringAnimation mFgSpringX; private final SpringAnimation mFgSpringX;
private float mFgTransX; private float mFgTransX;
private FloatingIconView(Launcher launcher) { public FloatingIconView(Context context) {
super(launcher); this(context, null);
mLauncher = launcher; }
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( mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline); R.dimen.blur_size_medium_outline);
mListenerView = new ListenerView(launcher, null); mListenerView = new ListenerView(context, attrs);
mFgSpringX = new SpringAnimation(this, mFgTransXProperty) mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
.setSpring(new SpringForce() .setSpring(new SpringForce()
@ -350,6 +361,7 @@ public class FloatingIconView extends View implements
} }
@WorkerThread @WorkerThread
@SuppressWarnings("WrongThread")
private void getIcon(View v, ItemInfo info, boolean isOpening, private void getIcon(View v, ItemInfo info, boolean isOpening,
Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) { Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) {
final LayoutParams lp = (LayoutParams) getLayoutParams(); final LayoutParams lp = (LayoutParams) getLayoutParams();
@ -396,7 +408,7 @@ public class FloatingIconView extends View implements
&& finalDrawable instanceof AdaptiveIconDrawable; && finalDrawable instanceof AdaptiveIconDrawable;
int iconOffset = getOffsetForIconBounds(finalDrawable); int iconOffset = getOffsetForIconBounds(finalDrawable);
new Handler(Looper.getMainLooper()).post(() -> { mLauncher.getMainExecutor().execute(() -> {
if (isAdaptiveIcon) { if (isAdaptiveIcon) {
mIsAdaptiveIcon = true; mIsAdaptiveIcon = true;
boolean isFolderIcon = finalDrawable instanceof FolderAdaptiveIcon; boolean isFolderIcon = finalDrawable instanceof FolderAdaptiveIcon;
@ -505,6 +517,7 @@ public class FloatingIconView extends View implements
} }
@WorkerThread @WorkerThread
@SuppressWarnings("WrongThread")
private int getOffsetForIconBounds(Drawable drawable) { private int getOffsetForIconBounds(Drawable drawable) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
!(drawable instanceof AdaptiveIconDrawable)) { !(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); Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline);
bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2); 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)); 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. * @param isOpening True if this view replaces the icon for app open animation.
*/ */
public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView, public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
boolean hideOriginal, RectF positionOut, boolean isOpening, FloatingIconView recycle) { boolean hideOriginal, RectF positionOut, boolean isOpening) {
if (recycle != null) { final DragLayer dragLayer = launcher.getDragLayer();
recycle.recycle(); ViewGroup parent = (ViewGroup) dragLayer.getParent();
}
FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher); FloatingIconView view = launcher.getViewCache().getView(R.layout.floating_icon_view,
launcher, parent);
view.recycle();
view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout(); view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
view.mOriginalIcon = originalView; view.mOriginalIcon = originalView;
@ -626,16 +642,15 @@ public class FloatingIconView extends View implements
originalView.setVisibility(INVISIBLE); originalView.setVisibility(INVISIBLE);
}; };
CancellationSignal loadIconSignal = view.mLoadIconSignal; CancellationSignal loadIconSignal = view.mLoadIconSignal;
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { new Handler(UiThreadHelper.getBackgroundLooper()).postAtFrontOfQueue(() -> {
view.getIcon(originalView, (ItemInfo) originalView.getTag(), isOpening, view.getIcon(originalView, (ItemInfo) originalView.getTag(), isOpening,
onIconLoaded, loadIconSignal); onIconLoaded, loadIconSignal);
}); });
} }
// We need to add it to the overlay, but keep it invisible until animation starts.. // We need to add it to the overlay, but keep it invisible until animation starts..
final DragLayer dragLayer = launcher.getDragLayer();
view.setVisibility(INVISIBLE); view.setVisibility(INVISIBLE);
((ViewGroup) dragLayer.getParent()).addView(view); parent.addView(view);
dragLayer.addView(view.mListenerView); dragLayer.addView(view.mListenerView);
view.mListenerView.setListener(view::onListenerViewClosed); view.mListenerView.setListener(view::onListenerViewClosed);
@ -714,6 +729,7 @@ public class FloatingIconView extends View implements
((ViewGroup) dragLayer.getParent()).removeView(this); ((ViewGroup) dragLayer.getParent()).removeView(this);
dragLayer.removeView(mListenerView); dragLayer.removeView(mListenerView);
recycle(); recycle();
mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this);
} }
private void recycle() { private void recycle() {