* task stacks and update recents accordingly.
*/
class TaskStackListenerImpl extends TaskStackListener {
+
@Override
- public void onTaskStackChanged() {
+ public void onTaskStackChangedBackground() {
// Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- SystemServicesProxy ssp = Recents.getSystemServices();
- ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
// Load the next task only if we aren't svelte
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+
// This callback is made when a new activity is launched and the old one is paused
// so ignore the current activity and try and preload the thumbnail for the
// previous one.
- if (runningTaskInfo != null) {
- launchOpts.runningTaskId = runningTaskInfo.id;
+ VisibilityReport visibilityReport;
+ synchronized (mDummyStackView) {
+ mDummyStackView.setTasks(plan.getTaskStack(), false /* allowNotify */);
+ updateDummyStackViewLayout(plan.getTaskStack(),
+ getWindowRect(null /* windowRectOverride */));
+
+ // Launched from app is always the worst case (in terms of how many
+ // thumbnails/tasks visible)
+ RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
+ launchState.launchedFromApp = true;
+ mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */, launchState);
+ visibilityReport = mDummyStackView.computeStackVisibilityReport();
}
- mDummyStackView.setTasks(plan.getTaskStack(), false /* allowNotify */);
- updateDummyStackViewLayout(plan.getTaskStack(),
- getWindowRect(null /* windowRectOverride */));
- // Launched from app is always the worst case (in terms of how many thumbnails/tasks
- // visible)
- RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
- launchState.launchedFromApp = true;
- mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */, launchState);
-
- VisibilityReport visibilityReport = mDummyStackView.computeStackVisibilityReport();
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
launchOpts.onlyLoadForCache = true;
}
public void onConfigurationChanged() {
- Resources res = mContext.getResources();
reloadResources();
- mDummyStackView.reloadOnConfigurationChange();
+ synchronized (mDummyStackView) {
+ mDummyStackView.reloadOnConfigurationChange();
+ }
}
/**
RecentsTaskLoader loader = Recents.getTaskLoader();
sInstanceLoadPlan = loader.createLoadPlan(mContext);
- sInstanceLoadPlan.preloadRawTasks(!isHomeStackVisible.value);
loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value);
TaskStack stack = sInstanceLoadPlan.getTaskStack();
if (stack.getTaskCount() > 0) {
calculateWindowStableInsets(systemInsets, windowRect);
windowRect.offsetTo(0, 0);
- TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
+ synchronized (mDummyStackView) {
+ TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
- // Rebind the header bar and draw it for the transition
- stackLayout.setSystemInsets(systemInsets);
- if (stack != null) {
- stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
- systemInsets.left, systemInsets.right, mTaskStackBounds);
- stackLayout.reset();
- stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
- TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
+ // Rebind the header bar and draw it for the transition
+ stackLayout.setSystemInsets(systemInsets);
+ if (stack != null) {
+ stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
+ systemInsets.left, systemInsets.right, mTaskStackBounds);
+ stackLayout.reset();
+ stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
+ }
}
}
*/
private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
Rect windowRect = getWindowRect(windowRectOverride);
- updateDummyStackViewLayout(stack, windowRect);
- if (stack != null) {
- TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
- mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
- // Get the width of a task view so that we know how wide to draw the header bar.
- int taskViewWidth = 0;
- if (mDummyStackView.useGridLayout()) {
- TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
- gridLayout.initialize(windowRect);
- taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
- stack.getTaskCount(), new TaskViewTransform(), stackLayout).rect.width();
- } else {
- Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
- if (!taskViewBounds.isEmpty()) {
- taskViewWidth = taskViewBounds.width();
+ int taskViewWidth = 0;
+ boolean useGridLayout = false;
+ synchronized (mDummyStackView) {
+ useGridLayout = mDummyStackView.useGridLayout();
+ updateDummyStackViewLayout(stack, windowRect);
+ if (stack != null) {
+ TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
+ mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
+ // Get the width of a task view so that we know how wide to draw the header bar.
+ if (useGridLayout) {
+ TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
+ gridLayout.initialize(windowRect);
+ taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
+ stack.getTaskCount(), new TaskViewTransform(),
+ stackLayout).rect.width();
+ } else {
+ Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
+ if (!taskViewBounds.isEmpty()) {
+ taskViewWidth = taskViewBounds.width();
+ }
}
}
+ }
- if (taskViewWidth > 0) {
- synchronized (mHeaderBarLock) {
- if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
- mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
- if (mDummyStackView.useGridLayout()) {
- mHeaderBar.setShouldDarkenBackgroundColor(true);
- mHeaderBar.setNoUserInteractionState();
- }
- mHeaderBar.forceLayout();
- mHeaderBar.measure(
- MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
+ if (stack != null && taskViewWidth > 0) {
+ synchronized (mHeaderBarLock) {
+ if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
+ mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
+ if (useGridLayout) {
+ mHeaderBar.setShouldDarkenBackgroundColor(true);
+ mHeaderBar.setNoUserInteractionState();
}
- mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+ mHeaderBar.forceLayout();
+ mHeaderBar.measure(
+ MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
}
+ mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+ }
- // Update the transition bitmap to match the new header bar height
- if (mThumbTransitionBitmapCache == null ||
- (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
- (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
- mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
- mTaskBarHeight, Bitmap.Config.ARGB_8888);
- }
+ // Update the transition bitmap to match the new header bar height
+ if (mThumbTransitionBitmapCache == null ||
+ (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
+ (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
+ mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
+ mTaskBarHeight, Bitmap.Config.ARGB_8888);
}
}
}
* Creates the activity options for an app->recents transition.
*/
private ActivityOptions getThumbnailTransitionActivityOptions(
- ActivityManager.RunningTaskInfo runningTask, TaskStackView stackView,
- Rect windowOverrideRect) {
+ ActivityManager.RunningTaskInfo runningTask, Rect windowOverrideRect) {
if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
- ArrayList<Task> tasks = stackView.getStack().getStackTasks();
- TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = stackView.getScroller();
+ ArrayList<Task> tasks;
+ TaskStackLayoutAlgorithm stackLayout;
+ TaskStackViewScroller stackScroller;
- stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
+ synchronized (mDummyStackView) {
+ tasks = mDummyStackView.getStack().getStackTasks();
+ stackLayout = mDummyStackView.getStackAlgorithm();
+ stackScroller = mDummyStackView.getScroller();
+
+ mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */);
+ mDummyStackView.updateToInitialState();
+ }
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
} else {
// Update the destination rect
Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask,
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
windowOverrideRect);
Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
mThumbTransitionBitmapCache);
updateHeaderBarLayout(stack, windowOverrideRect);
// Prepare the dummy stack for the transition
- TaskStackLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
+ TaskStackLayoutAlgorithm.VisibilityReport stackVr;
+ synchronized (mDummyStackView) {
+ stackVr = mDummyStackView.computeStackVisibilityReport();
+ }
// Update the remaining launch state
launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
opts = getUnknownTransitionActivityOptions();
} else if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- opts = getThumbnailTransitionActivityOptions(runningTask, mDummyStackView,
- windowOverrideRect);
+ opts = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
} else {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition
* to reduce IPC calls from system services. These callbacks will be called on the main thread.
*/
public abstract static class TaskStackListener {
+ /**
+ * NOTE: This call is made of the thread that the binder call comes in on.
+ */
+ public void onTaskStackChangedBackground() { }
public void onTaskStackChanged() { }
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
public void onActivityPinned(String packageName) { }
* This simply passes callbacks to listeners through {@link H}.
* */
private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
+
+ private final List<SystemServicesProxy.TaskStackListener> mTmpListeners = new ArrayList<>();
+
@Override
public void onTaskStackChanged() throws RemoteException {
+ // Call the task changed callback for the non-ui thread listeners first
+ synchronized (mTaskStackListeners) {
+ mTmpListeners.clear();
+ mTmpListeners.addAll(mTaskStackListeners);
+ }
+ for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
+ mTmpListeners.get(i).onTaskStackChangedBackground();
+ }
+
mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
}
* Returns the single instance of the {@link SystemServicesProxy}.
* This should only be called on the main thread.
*/
- public static SystemServicesProxy getInstance(Context context) {
- if (!Looper.getMainLooper().isCurrentThread()) {
- throw new RuntimeException("Must be called on the UI thread");
- }
+ public static synchronized SystemServicesProxy getInstance(Context context) {
if (sSystemServicesProxy == null) {
sSystemServicesProxy = new SystemServicesProxy(context);
}
public void registerTaskStackListener(TaskStackListener listener) {
if (mIam == null) return;
- mTaskStackListeners.add(listener);
- if (mTaskStackListeners.size() == 1) {
- // Register mTaskStackListener to IActivityManager only once if needed.
- try {
- mIam.registerTaskStackListener(mTaskStackListener);
- } catch (Exception e) {
- Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ synchronized (mTaskStackListeners) {
+ mTaskStackListeners.add(listener);
+ if (mTaskStackListeners.size() == 1) {
+ // Register mTaskStackListener to IActivityManager only once if needed.
+ try {
+ mIam.registerTaskStackListener(mTaskStackListener);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ }
}
}
}
@Override
public void handleMessage(Message msg) {
- switch (msg.what) {
- case ON_TASK_STACK_CHANGED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskStackChanged();
+ synchronized (mTaskStackListeners) {
+ switch (msg.what) {
+ case ON_TASK_STACK_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskStackChanged();
+ }
+ break;
}
- break;
- }
- case ON_TASK_SNAPSHOT_CHANGED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
- (TaskSnapshot) msg.obj);
+ case ON_TASK_SNAPSHOT_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
+ (TaskSnapshot) msg.obj);
+ }
+ break;
}
- break;
- }
- case ON_ACTIVITY_PINNED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityPinned((String) msg.obj);
+ case ON_ACTIVITY_PINNED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityPinned((String) msg.obj);
+ }
+ break;
}
- break;
- }
- case ON_ACTIVITY_UNPINNED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityUnpinned();
+ case ON_ACTIVITY_UNPINNED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityUnpinned();
+ }
+ break;
}
- break;
- }
- case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ }
+ break;
}
- break;
- }
- case ON_PINNED_STACK_ANIMATION_STARTED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
+ case ON_PINNED_STACK_ANIMATION_STARTED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
+ }
+ break;
}
- break;
- }
- case ON_PINNED_STACK_ANIMATION_ENDED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
+ case ON_PINNED_STACK_ANIMATION_ENDED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
+ }
+ break;
}
- break;
- }
- case ON_ACTIVITY_FORCED_RESIZABLE: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityForcedResizable(
- (String) msg.obj, msg.arg1, msg.arg2);
+ case ON_ACTIVITY_FORCED_RESIZABLE: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
}
- break;
- }
- case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityDismissingDockedStack();
+ case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityDismissingDockedStack();
+ }
+ break;
}
- break;
- }
- case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
+ case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
+ }
+ break;
}
- break;
- }
- case ON_TASK_PROFILE_LOCKED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
+ case ON_TASK_PROFILE_LOCKED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
+ }
+ break;
}
- break;
}
}
}
import android.util.Log;
import android.util.LruCache;
+import com.android.internal.annotations.GuardedBy;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
private final TaskResourceLoadQueue mLoadQueue;
private final BackgroundTaskLoader mLoader;
private final HighResThumbnailLoader mHighResThumbnailLoader;
+ @GuardedBy("this")
private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
+ @GuardedBy("this")
private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
private final int mMaxThumbnailCacheSize;
private final int mMaxIconCacheSize;
return plan;
}
+ /** Preloads raw recents tasks using the specified plan to store the output. */
+ public synchronized void preloadRawTasks(RecentsTaskLoadPlan plan,
+ boolean includeFrontMostExcludedTask) {
+ plan.preloadRawTasks(includeFrontMostExcludedTask);
+ }
+
/** Preloads recents tasks using the specified plan to store the output. */
- public void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
+ public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
boolean includeFrontMostExcludedTask) {
plan.preloadPlan(this, runningTaskId, includeFrontMostExcludedTask);
}
/** Begins loading the heavy task data according to the specified options. */
- public void loadTasks(Context context, RecentsTaskLoadPlan plan,
+ public synchronized void loadTasks(Context context, RecentsTaskLoadPlan plan,
RecentsTaskLoadPlan.Options opts) {
if (opts == null) {
throw new RuntimeException("Requires load options");
* Handles signals from the system, trimming memory when requested to prevent us from running
* out of memory.
*/
- public void onTrimMemory(int level) {
- RecentsConfiguration config = Recents.getConfiguration();
+ public synchronized void onTrimMemory(int level) {
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
// Stop the loader immediately when the UI is no longer visible
/**
* Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
*/
- ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached,
+ synchronized ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached,
boolean storeInCache) {
SystemServicesProxy ssp = Recents.getSystemServices();
}
}
- public void dump(String prefix, PrintWriter writer) {
+ public synchronized void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
writer.print(prefix); writer.println(TAG);