Merge "Recycling taskViews instead of inflating & throwing them away everytime" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot 2019-01-11 07:20:55 +00:00 committed by Android (Google) Code Review
commit 1a956e812c
8 changed files with 150 additions and 36 deletions

View File

@ -42,14 +42,8 @@
android:importantForAccessibility="noHideDescendants"
android:background="#800000FF"
android:layout_gravity="bottom"
android:gravity="center"
android:textColor="@android:color/white"
android:visibility="gone"
>
<TextView
android:id="@+id/remaining_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="@android:color/white"
/>
</com.android.quickstep.views.DigitalWellBeingToast>
</com.android.quickstep.views.TaskView>

View File

@ -16,16 +16,13 @@
package com.android.quickstep;
import android.content.Context;
import android.graphics.Matrix;
import android.view.View;
import androidx.annotation.AnyThread;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@ -34,11 +31,12 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.AnyThread;
/**
* Factory class to create and add an overlays on the TaskView
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
private static TaskOverlayFactory sInstance;
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
@ -49,14 +47,9 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
new TaskSystemShortcut.Freeform()
};
public static TaskOverlayFactory get(Context context) {
Preconditions.assertUIThread();
if (sInstance == null) {
sInstance = Overrides.getObject(TaskOverlayFactory.class,
context.getApplicationContext(), R.string.task_overlay_factory_class);
}
return sInstance;
}
public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
c, R.string.task_overlay_factory_class));
@AnyThread
public boolean needAssist() {

View File

@ -203,7 +203,7 @@ public class TouchInteractionService extends Service {
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
mOverviewCallbacks = OverviewCallbacks.get(this);
mTaskOverlayFactory = TaskOverlayFactory.get(this);
mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
mTouchInteractionLog = new TouchInteractionLog();
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();

View File

@ -23,7 +23,6 @@ import android.content.Intent;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.Launcher;
@ -32,7 +31,8 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
public final class DigitalWellBeingToast extends LinearLayout {
public final class DigitalWellBeingToast extends TextView {
public interface InitializeCallback {
void call(float saturation, String contentDescription);
}
@ -56,12 +56,11 @@ public final class DigitalWellBeingToast extends LinearLayout {
final long appRemainingTimeMs = -1;
final boolean isGroupLimit = true;
post(() -> {
final TextView remainingTimeText = findViewById(R.id.remaining_time);
if (appUsageLimitTimeMs < 0) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
remainingTimeText.setText(getText(appRemainingTimeMs, isGroupLimit));
setText(getText(appRemainingTimeMs, isGroupLimit));
}
callback.call(

View File

@ -81,6 +81,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.QuickScrubController;
import com.android.quickstep.RecentsAnimationWrapper;
@ -156,6 +157,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private final InvariantDeviceProfile mIdp;
private final ViewPool<TaskView> mTaskViewPool;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@ -304,6 +307,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
mIsRtl = !Utilities.isRtl(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
@ -384,6 +390,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mHasVisibleTaskData.delete(task.key.id);
taskView.onTaskListVisibilityChanged(false /* visible */);
}
mTaskViewPool.recycle(taskView);
}
}
@ -487,10 +494,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
int oldChildCount = getChildCount();
// Ensure there are as many views as there are tasks in the stack (adding and trimming as
// necessary)
final LayoutInflater inflater = LayoutInflater.from(getContext());
// Unload existing visible task data
unloadVisibleTaskData();
@ -503,7 +506,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
removeView(mClearAllButton);
}
for (int i = getChildCount(); i < requiredTaskCount; i++) {
addView(inflater.inflate(R.layout.task, this, false));
addView(mTaskViewPool.getView());
}
while (getChildCount() > requiredTaskCount) {
removeView(getChildAt(getChildCount() - 1));
@ -754,8 +757,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
public void showTask(int runningTaskId) {
if (getChildCount() == 0) {
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
.inflate(R.layout.task, this, false);
final TaskView taskView = mTaskViewPool.getView();
addView(taskView);
addView(mClearAllButton);

View File

@ -107,7 +107,7 @@ public class TaskThumbnailView extends View {
public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
mPaint.setFilterBitmap(true);
mBackgroundPaint.setColor(Color.WHITE);
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

View File

@ -52,6 +52,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
@ -70,7 +71,7 @@ import java.util.function.Consumer;
/**
* A task in the Recents view.
*/
public class TaskView extends FrameLayout implements PageCallbacks {
public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private static final String TAG = TaskView.class.getSimpleName();
@ -399,19 +400,29 @@ public class TaskView extends FrameLayout implements PageCallbacks {
setIconAndDimTransitionProgress(iconScale, invert);
}
public void resetVisualProperties() {
private void resetViewTransforms() {
setZoomScale(1);
setTranslationX(0f);
setTranslationY(0f);
setTranslationZ(0);
setAlpha(1f);
setIconScaleAndDim(1);
}
public void resetVisualProperties() {
resetViewTransforms();
if (!getRecentsView().getQuickScrubController().isQuickSwitch()) {
// Reset full screen progress unless we are doing back to back quick switch.
setFullscreenProgress(0);
}
}
@Override
public void onRecycle() {
resetViewTransforms();
setFullscreenProgress(0);
}
@Override
public void onPageScroll(ScrollState scrollState) {
float curveInterpolation =

View File

@ -0,0 +1,115 @@
/*
* 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.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.util.ViewPool.Reusable;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
/**
* Utility class to maintain a pool of reusable views.
* During initialization, views are inflated on the background thread.
*/
public class ViewPool<T extends View & Reusable> {
private final Object[] mPool;
private final LayoutInflater mInflater;
private final ViewGroup mParent;
private final int mLayoutId;
private int mCurrentSize = 0;
public ViewPool(Context context, @Nullable ViewGroup parent,
int layoutId, int maxSize, int initialSize) {
mLayoutId = layoutId;
mParent = parent;
mInflater = LayoutInflater.from(context);
mPool = new Object[maxSize];
if (initialSize > 0) {
initPool(initialSize);
}
}
@UiThread
private void initPool(int initialSize) {
Preconditions.assertUIThread();
Handler handler = new Handler();
// Inflate views on a non looper thread. This allows us to catch errors like calling
// "new Handler()" in constructor easily.
new Thread(() -> {
for (int i = 0; i < initialSize; i++) {
T view = inflateNewView();
handler.post(() -> addToPool(view));
}
}).start();
}
@UiThread
public void recycle(T view) {
Preconditions.assertUIThread();
view.onRecycle();
addToPool(view);
}
@UiThread
private void addToPool(T view) {
Preconditions.assertUIThread();
if (mCurrentSize >= mPool.length) {
// pool is full
return;
}
mPool[mCurrentSize] = view;
mCurrentSize++;
}
@UiThread
public T getView() {
Preconditions.assertUIThread();
if (mCurrentSize > 0) {
mCurrentSize--;
return (T) mPool[mCurrentSize];
}
return inflateNewView();
}
@AnyThread
private T inflateNewView() {
return (T) mInflater.inflate(mLayoutId, mParent, false);
}
/**
* Interface to indicate that a view is reusable
*/
public interface Reusable {
/**
* Called when a view is recycled / added back to the pool
*/
void onRecycle();
}
}