diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index 881aa6ac688fc..5cd540bcb789d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -26,10 +26,6 @@ import com.android.systemui.tuner.TunerService; */ public class RecentsDebugFlags implements TunerService.Tunable { - private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button"; - private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator"; - private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging"; - public static class Static { // Enables debug drawing for the transition thumbnail public static final boolean EnableTransitionThumbnailDebugMode = false; @@ -39,18 +35,23 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static final boolean DisableBackgroundCache = false; // Enables the task affiliations public static final boolean EnableAffiliatedTaskGroups = true; - // Enables the simulated task affiliations - public static final boolean EnableSimulatedTaskGroups = false; - // Defines the number of mock task affiliations per group - public static final int TaskAffiliationsGroupCount = 12; + // Enables us to create mock recents tasks - public static final boolean EnableSystemServicesProxy = false; + public static final boolean EnableMockTasks = false; // Defines the number of mock recents packages to create - public static final int SystemServicesProxyMockPackageCount = 3; + public static final int MockTasksPackageCount = 3; // Defines the number of mock recents tasks to create - public static final int SystemServicesProxyMockTaskCount = 100; + public static final int MockTaskCount = 100; + // Enables the simulated task affiliations + public static final boolean EnableMockTaskGroups = false; + // Defines the number of mock task affiliations per group + public static final int MockTaskGroupsTaskCount = 12; } + private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button"; + private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator"; + private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging"; + private boolean mFastToggleRecents; private boolean mFastToggleIndicator; private boolean mInitialStatePaging; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 3f52ae8d7ed15..87cfcff1066e3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -151,7 +151,7 @@ public class SystemServicesProxy { // Resolve the assist intent mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()); - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { // Create a dummy icon mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); mDummyIcon.eraseColor(0xFF999999); @@ -164,20 +164,20 @@ public class SystemServicesProxy { if (mAm == null) return null; // If we are mocking, then create some recent tasks - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { ArrayList tasks = new ArrayList(); - int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.SystemServicesProxyMockTaskCount); + int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount); for (int i = 0; i < count; i++) { // Create a dummy component name - int packageIndex = i % RecentsDebugFlags.Static.SystemServicesProxyMockPackageCount; + int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount; ComponentName cn = new ComponentName("com.android.test" + packageIndex, "com.android.test" + i + ".Activity"); String description = "" + i + " - " + Long.toString(Math.abs(new Random().nextLong()), 36); // Create the recent task info ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); - rti.id = rti.persistentId = i; + rti.id = rti.persistentId = rti.affiliatedTaskId = i; rti.baseIntent = new Intent(); rti.baseIntent.setComponent(cn); rti.description = description; @@ -418,7 +418,7 @@ public class SystemServicesProxy { if (mAm == null) return null; // If we are mocking, then just return a dummy thumbnail - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight, Bitmap.Config.ARGB_8888); thumbnail.eraseColor(0xff333333); @@ -484,7 +484,7 @@ public class SystemServicesProxy { /** Moves a task to the front with the specified activity options. */ public void moveTaskToFront(int taskId, ActivityOptions opts) { if (mAm == null) return; - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return; + if (RecentsDebugFlags.Static.EnableMockTasks) return; if (opts != null) { mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME, @@ -497,7 +497,7 @@ public class SystemServicesProxy { /** Removes the task */ public void removeTask(final int taskId) { if (mAm == null) return; - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return; + if (RecentsDebugFlags.Static.EnableMockTasks) return; // Remove the task. BackgroundThread.getHandler().post(new Runnable() { @@ -528,7 +528,7 @@ public class SystemServicesProxy { */ public ActivityInfo getActivityInfo(ComponentName cn, int userId) { if (mIpm == null) return null; - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo(); + if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo(); try { return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId); @@ -545,7 +545,7 @@ public class SystemServicesProxy { */ public ActivityInfo getActivityInfo(ComponentName cn) { if (mPm == null) return null; - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo(); + if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo(); try { return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA); @@ -562,7 +562,7 @@ public class SystemServicesProxy { if (mPm == null) return null; // If we are mocking, then return a mock label - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { return "Recent Task: " + userId; } @@ -576,7 +576,7 @@ public class SystemServicesProxy { if (mPm == null) return null; // If we are mocking, then return a mock label - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { return "Recent Task App: " + userId; } @@ -588,6 +588,11 @@ public class SystemServicesProxy { * description joins the app and activity labels. */ public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) { + // If we are mocking, then return a mock label + if (RecentsDebugFlags.Static.EnableMockTasks) { + return "Recent Task Content Description: " + userId; + } + String activityLabel = info.loadLabel(mPm).toString(); String applicationLabel = info.applicationInfo.loadLabel(mPm).toString(); String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); @@ -604,7 +609,7 @@ public class SystemServicesProxy { if (mPm == null) return null; // If we are mocking, then return a mock label - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { return new ColorDrawable(0xFF666666); } @@ -620,7 +625,7 @@ public class SystemServicesProxy { if (mPm == null) return null; // If we are mocking, then return a mock label - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { return new ColorDrawable(0xFF666666); } @@ -635,7 +640,7 @@ public class SystemServicesProxy { int userId, Resources res) { // If we are mocking, then return a mock label - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) { + if (RecentsDebugFlags.Static.EnableMockTasks) { return new ColorDrawable(0xFF666666); } @@ -673,7 +678,7 @@ public class SystemServicesProxy { /** Returns the package name of the home activity. */ public String getHomeActivityPackageName() { if (mPm == null) return null; - if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null; + if (RecentsDebugFlags.Static.EnableMockTasks) return null; ArrayList homeActivities = new ArrayList<>(); ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 9cdd703852fe3..d15828a965548 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -22,6 +22,7 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.os.Debug; import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; @@ -31,6 +32,7 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.misc.SystemServicesProxy; import java.util.ArrayList; @@ -130,6 +132,9 @@ public class RecentsTaskLoadPlan { R.string.accessibility_recents_item_will_be_dismissed); long lastStackActiveTime = Prefs.getLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0); + if (RecentsDebugFlags.Static.EnableMockTasks) { + lastStackActiveTime = 0; + } long newLastStackActiveTime = -1; int taskCount = mRawTasks.size(); for (int i = 0; i < taskCount; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index de1daa8ea988f..66eeac60d8013 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -776,7 +776,7 @@ public class TaskStack { * Temporary: This method will simulate affiliation groups by */ public void createAffiliatedGroupings(Context context) { - if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) { + if (RecentsDebugFlags.Static.EnableMockTaskGroups) { ArrayMap taskMap = new ArrayMap<>(); // Sort all tasks by increasing firstActiveTime of the task ArrayList tasks = mStackTaskList.getTasks(); @@ -792,7 +792,7 @@ public class TaskStack { String prevPackage = ""; int prevAffiliation = -1; Random r = new Random(); - int groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount; + int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount; for (int i = 0; i < taskCount; i++) { Task t = tasks.get(i); String packageName = t.key.getComponent().getPackageName(); @@ -807,7 +807,7 @@ public class TaskStack { addGroup(group); prevAffiliation = affiliation; prevPackage = packageName; - groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount; + groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount; } group.addTask(t); taskMap.put(t.key, t); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java new file mode 100644 index 0000000000000..9f2b00a50e606 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 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.systemui.recents.views; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * This is an optimized FrameLayout whose layout is completely directed by its parent, and as a + * result, does not propagate requestLayout() up the view hierarchy. Instead, it will + * relayout its children with the last known layout bounds when a layout is requested from a child + * view. + */ +public class FixedSizeFrameLayout extends FrameLayout { + + private final Rect mLayoutBounds = new Rect(); + + public FixedSizeFrameLayout(Context context) { + super(context); + } + + public FixedSizeFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureContents(MeasureSpec.getSize(widthMeasureSpec), + MeasureSpec.getSize(heightMeasureSpec)); + } + + @Override + protected final void onLayout(boolean changed, int left, int top, int right, int bottom) { + mLayoutBounds.set(left, top, right, bottom); + layoutContents(mLayoutBounds, changed); + } + + @Override + public final void requestLayout() { + // The base ViewGroup constructor attempts to call requestLayout() before this class's + // members are initialized so we should just propagate in that case + if (mLayoutBounds == null || mLayoutBounds.isEmpty()) { + super.requestLayout(); + } else { + // If we are already laid out, then just reuse the same bounds to layout the children + // (but not itself) + // TODO: Investigate whether we should coalesce these to the next frame if needed + measureContents(getMeasuredWidth(), getMeasuredHeight()); + layoutContents(mLayoutBounds, false); + } + } + + /** + * Measures the contents of this fixed layout. + */ + protected void measureContents(int width, int height) { + super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + } + + /** + * Lays out the contents of this fixed layout. + */ + protected void layoutContents(Rect bounds, boolean changed) { + super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java index 3f5d0a8462999..f5ab01f5bf5b6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java @@ -23,13 +23,13 @@ import android.util.AttributeSet; import android.widget.ImageView; /** - * This is an optimized ImageView that does not trigger a requestLayout() or invalidate() when - * setting the image to Null. + * This is an optimized ImageView that does not trigger a requestLayout() or + * invalidate() when setting the image to null. */ public class FixedSizeImageView extends ImageView { - boolean mAllowRelayout = true; - boolean mAllowInvalidate = true; + private boolean mAllowRelayout = true; + private boolean mAllowInvalidate = true; public FixedSizeImageView(Context context) { this(context, null); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index fe9c68eaf2395..ccc858100d483 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1375,11 +1375,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Report that this tasks's data is no longer being used Recents.getTaskLoader().unloadTaskData(task); - // Detach the view from the hierarchy - detachViewFromParent(tv); - // Update the task views list after removing the task view - updateTaskViewsList(); - // Reset the view properties and view state tv.resetViewProperties(); tv.setFocusedState(false, false /* requestViewFocus */); @@ -1387,19 +1382,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (mScreenPinningEnabled) { tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null); } + + // Detach the view from the hierarchy + detachViewFromParent(tv); + // Update the task views list after removing the task view + updateTaskViewsList(); } @Override public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) { - // Rebind the task and request that this task's data be filled into the TaskView - tv.onTaskBound(task); - - // Load the task data - Recents.getTaskLoader().loadTaskData(task); - - // If the doze trigger has already fired, then update the state for this task view - tv.setNoUserInteractionState(); - // Find the index where this task should be placed in the stack int taskIndex = mStack.indexOfStackTask(task); int insertIndex = findTaskViewInsertIndex(task, taskIndex); @@ -1413,6 +1404,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Update the task views list after adding the new task view updateTaskViewsList(); + // Rebind the task and request that this task's data be filled into the TaskView + tv.onTaskBound(task); + + // Load the task data + Recents.getTaskLoader().loadTaskData(task); + + // If the doze trigger has already fired, then update the state for this task view + tv.setNoUserInteractionState(); + // Set the new state for this view, including the callbacks and view clipping tv.setCallbacks(this); tv.setTouchEnabled(true); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 32bebb36d17ff..5a4064a686bf9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -17,7 +17,6 @@ package com.android.systemui.recents.views; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -40,7 +39,6 @@ import android.view.ViewOutlineProvider; import android.view.animation.AccelerateInterpolator; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivity; @@ -62,8 +60,13 @@ import java.util.ArrayList; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -/* A task view */ -public class TaskView extends FrameLayout implements Task.TaskCallbacks, +/** + * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed + * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down + * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself + * with the previous bounds if any child requests layout). + */ +public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks, TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener { /** The TaskView callbacks */ @@ -219,33 +222,20 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, return super.onInterceptTouchEvent(ev); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = MeasureSpec.getSize(widthMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); + @Override + protected void measureContents(int width, int height) { int widthWithoutPadding = width - mPaddingLeft - mPaddingRight; int heightWithoutPadding = height - mPaddingTop - mPaddingBottom; - int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height); // Measure the content mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY)); - // Measure the bar view, and action button - mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY)); - mActionButtonView.measure( - MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST)); - // Measure the thumbnail to be square - mThumbnailView.measure( - MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY)); + // Optimization: Prevent overdraw of the thumbnail under the header view mThumbnailView.updateClipToTaskBar(mHeaderView); setMeasuredDimension(width, height); - invalidateOutline(); } void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 827ee4088d57f..cb108daab3ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -245,11 +245,7 @@ public class TaskViewHeader extends FrameLayout * to match the frame changes. */ public void onTaskViewSizeChanged(int width, int height) { - // Return early if the bounds have not changed - if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) { - return; - } - + // TODO: Optimize this path mTaskViewRect.set(0, 0, width, height); boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE; boolean isFreeformTask = (mTask != null) && mTask.isFreeformTask(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 39d06049edafe..de96d9da8e858 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -190,7 +190,6 @@ public class TaskViewThumbnail extends View { if (!mInvisible) { updateThumbnailPaintFilter(); } - invalidate(); } }