Defer some work until after workspace fade-in

Defer:
- Setting all apps
- Setting widgets

Also set the launcher-loader thread to THREAD_PRIORITY_BACKGROUND
for the duration of the animation.

Bug: 37965432
Change-Id: I8364940805b84aecb8353a473ab4d575c27bfec4
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7c25861..615ec47 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -144,6 +144,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * Default launcher application.
@@ -1855,6 +1856,8 @@
 
         LauncherAnimUtils.onDestroyActivity();
 
+        clearPendingBinds();
+
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onDestroy();
         }
@@ -3705,6 +3708,13 @@
         }
 
         if (mAppsView != null) {
+            Executor pendingExecutor = getPendingExecutor();
+            if (pendingExecutor != null && mState != State.APPS) {
+                // Wait until the fade in animation has finished before setting all apps list.
+                mTmpAppsList = apps;
+                pendingExecutor.execute(mBindAllApplicationsRunnable);
+                return;
+            }
             mAppsView.setApps(apps);
         }
         if (mLauncherCallbacks != null) {
@@ -3713,6 +3723,14 @@
     }
 
     /**
+     * Returns an Executor that will run after the launcher is first drawn (including after the
+     * initial fade in animation). Returns null if the first draw has already occurred.
+     */
+    public @Nullable Executor getPendingExecutor() {
+        return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
+    }
+
+    /**
      * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
      */
@@ -3904,6 +3922,12 @@
         }
 
         if (mWidgetsView != null && allWidgets != null) {
+            Executor pendingExecutor = getPendingExecutor();
+            if (pendingExecutor != null && mState != State.WIDGETS) {
+                mAllWidgets = allWidgets;
+                pendingExecutor.execute(mBindAllWidgetsRunnable);
+                return;
+            }
             mWidgetsView.setWidgets(allWidgets);
             mAllWidgets = null;
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f1638fd..1007748 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -689,4 +689,8 @@
     public static Looper getWorkerLooper() {
         return sWorkerThread.getLooper();
     }
+
+    public static void setWorkerPriority(final int priority) {
+        Process.setThreadPriority(sWorkerThread.getThreadId(), priority);
+    }
 }
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 4cb6ca8..e5c1dd1 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,11 +16,13 @@
 
 package com.android.launcher3.util;
 
+import android.os.Process;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewTreeObserver.OnDrawListener;
 
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
 
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
@@ -37,6 +39,7 @@
     private Launcher mLauncher;
     private View mAttachedView;
     private boolean mCompleted;
+    private boolean mIsExecuting;
 
     private boolean mLoadAnimationCompleted;
     private boolean mFirstDrawCompleted;
@@ -62,6 +65,7 @@
     @Override
     public void execute(Runnable command) {
         mTasks.add(command);
+        LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
     }
 
     @Override
@@ -78,6 +82,13 @@
         mAttachedView.post(this);
     }
 
+    /**
+     * Returns whether the executor is still queuing tasks and hasn't yet executed them.
+     */
+    public boolean canQueue() {
+        return !mIsExecuting && !mCompleted;
+    }
+
     public void onLoadAnimationCompleted() {
         mLoadAnimationCompleted = true;
         if (mAttachedView != null) {
@@ -89,6 +100,7 @@
     public void run() {
         // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
         if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
+            mIsExecuting = true;
             for (final Runnable r : mTasks) {
                 mExecutor.execute(r);
             }
@@ -99,6 +111,7 @@
     public void markCompleted() {
         mTasks.clear();
         mCompleted = true;
+        mIsExecuting = false;
         if (mAttachedView != null) {
             mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
             mAttachedView.removeOnAttachStateChangeListener(this);
@@ -106,5 +119,6 @@
         if (mLauncher != null) {
             mLauncher.clearPendingExecutor(this);
         }
+        LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT);
     }
 }