Hack to wait for both suggestion/category to load

- This is unfortunately necessary to avoid a jank when category
  load completes before suggestion load, in which case user has to
  manually scroll up to see the suggestions.
- We could technically just add scrollTo(0), but that causes the list
  scroll on its own within 0.5 second of settings start, and that's bad.

Change-Id: I8dc869a69a5bf11bbf7644b281cc1778dd1a90e8
Fixes: 69068691
Test: visual
Test: robotests
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index 54a1eeb..3d473f0 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -161,7 +161,6 @@
     }
 
     public void setSuggestionsV2(List<Suggestion> data) {
-        // TODO: Tint icon
         final DashboardData prevData = mDashboardData;
         mDashboardData = new DashboardData.Builder(prevData)
                 .setSuggestionsV2(data)
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 8f66b67..ff2a76a 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -81,6 +81,9 @@
     private boolean isOnCategoriesChangedCalled;
     private boolean mOnConditionsChangedCalled;
 
+    private DashboardCategory mStagingCategory;
+    private List<Suggestion> mStagingSuggestions;
+
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.DASHBOARD_SUMMARY;
@@ -291,7 +294,13 @@
 
     @Override
     public void onSuggestionReady(List<Suggestion> suggestions) {
+        mStagingSuggestions = suggestions;
         mAdapter.setSuggestionsV2(suggestions);
+        if (mStagingCategory != null) {
+            Log.d(TAG, "Category has loaded, setting category from suggestionReady");
+            mHandler.removeCallbacksAndMessages(null);
+            mAdapter.setCategory(mStagingCategory);
+        }
     }
 
     /**
@@ -342,7 +351,19 @@
         final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
                 CategoryKey.CATEGORY_HOMEPAGE);
         mSummaryLoader.updateSummaryToCache(category);
-        ThreadUtils.postOnMainThread(() -> mAdapter.setCategory(category));
+        mStagingCategory = category;
+        if (mSuggestionControllerMixin.isSuggestionLoaded()) {
+            Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
+            ThreadUtils.postOnMainThread(() -> {
+                if (mStagingSuggestions != null) {
+                    mAdapter.setSuggestionsV2(mStagingSuggestions);
+                }
+                mAdapter.setCategory(mStagingCategory);
+            });
+        } else {
+            Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
+            mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
+        }
     }
 
     /**
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixin.java b/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixin.java
index 71bf107..81496ee 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixin.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixin.java
@@ -59,6 +59,8 @@
     private final SuggestionController mSuggestionController;
     private final SuggestionControllerHost mHost;
 
+    private boolean mSuggestionLoaded;
+
     public SuggestionControllerMixin(Context context, SuggestionControllerHost host,
             Lifecycle lifecycle) {
         mContext = context.getApplicationContext();
@@ -106,6 +108,7 @@
     @Override
     public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) {
         if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) {
+            mSuggestionLoaded = false;
             return new SuggestionLoader(mContext, mSuggestionController);
         }
         throw new IllegalArgumentException("This loader id is not supported " + id);
@@ -113,12 +116,17 @@
 
     @Override
     public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) {
+        mSuggestionLoaded = true;
         mHost.onSuggestionReady(data);
     }
 
     @Override
     public void onLoaderReset(Loader<List<Suggestion>> loader) {
+        mSuggestionLoaded = false;
+    }
 
+    public boolean isSuggestionLoaded() {
+        return mSuggestionLoaded;
     }
 
     public void dismissSuggestion(Suggestion suggestion) {
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixinTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixinTest.java
index ad97e18..790f166 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixinTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionControllerMixinTest.java
@@ -18,9 +18,7 @@
 
 import static android.arch.lifecycle.Lifecycle.Event.ON_START;
 import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
-
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,9 +35,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsRobolectricTestRunner.class)
@@ -49,19 +47,18 @@
         })
 public class SuggestionControllerMixinTest {
 
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
     @Mock
     private SuggestionControllerMixin.SuggestionControllerHost mHost;
+    private Context mContext;
     private Lifecycle mLifecycle;
     private SuggestionControllerMixin mMixin;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        FakeFeatureFactory.setupForTest(mContext);
+        mContext = RuntimeEnvironment.application;
+        FakeFeatureFactory.setupForTest();
         mLifecycle = new Lifecycle(() -> mLifecycle);
-        when(mContext.getApplicationContext()).thenReturn(mContext);
     }
 
     @After
@@ -111,4 +108,17 @@
 
         verify(mHost).getLoaderManager();
     }
+
+    @Test
+    public void doneLoadingg_shouldSetSuggestionLoaded() {
+        mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle);
+
+        mMixin.onLoadFinished(mock(SuggestionLoader.class), null);
+
+        assertThat(mMixin.isSuggestionLoaded()).isTrue();
+
+        mMixin.onLoaderReset(mock(SuggestionLoader.class));
+
+        assertThat(mMixin.isSuggestionLoaded()).isFalse();
+    }
 }