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();
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() {

View File

@ -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<T extends BaseActivity> 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<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() {
return mTempClipAnimationHelper;
}

View File

@ -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);

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 -->
<item type="id" name="view_unhighlight_background" />
<item type="id" name="view_highlighted" />
<!-- Menu id for feature 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.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;

View File

@ -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];

View File

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