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
This commit is contained in:
Fan Zhang
2017-12-04 14:31:30 -08:00
parent d786fc5bdd
commit cbc97bc7dd
4 changed files with 47 additions and 9 deletions

View File

@@ -161,7 +161,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
} }
public void setSuggestionsV2(List<Suggestion> data) { public void setSuggestionsV2(List<Suggestion> data) {
// TODO: Tint icon
final DashboardData prevData = mDashboardData; final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData) mDashboardData = new DashboardData.Builder(prevData)
.setSuggestionsV2(data) .setSuggestionsV2(data)

View File

@@ -81,6 +81,9 @@ public class DashboardSummary extends InstrumentedFragment
private boolean isOnCategoriesChangedCalled; private boolean isOnCategoriesChangedCalled;
private boolean mOnConditionsChangedCalled; private boolean mOnConditionsChangedCalled;
private DashboardCategory mStagingCategory;
private List<Suggestion> mStagingSuggestions;
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
return MetricsEvent.DASHBOARD_SUMMARY; return MetricsEvent.DASHBOARD_SUMMARY;
@@ -291,7 +294,13 @@ public class DashboardSummary extends InstrumentedFragment
@Override @Override
public void onSuggestionReady(List<Suggestion> suggestions) { public void onSuggestionReady(List<Suggestion> suggestions) {
mStagingSuggestions = suggestions;
mAdapter.setSuggestionsV2(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 @@ public class DashboardSummary extends InstrumentedFragment
final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory( final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
CategoryKey.CATEGORY_HOMEPAGE); CategoryKey.CATEGORY_HOMEPAGE);
mSummaryLoader.updateSummaryToCache(category); 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);
}
} }
/** /**

View File

@@ -59,6 +59,8 @@ public class SuggestionControllerMixin implements SuggestionController.ServiceCo
private final SuggestionController mSuggestionController; private final SuggestionController mSuggestionController;
private final SuggestionControllerHost mHost; private final SuggestionControllerHost mHost;
private boolean mSuggestionLoaded;
public SuggestionControllerMixin(Context context, SuggestionControllerHost host, public SuggestionControllerMixin(Context context, SuggestionControllerHost host,
Lifecycle lifecycle) { Lifecycle lifecycle) {
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
@@ -106,6 +108,7 @@ public class SuggestionControllerMixin implements SuggestionController.ServiceCo
@Override @Override
public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) { public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) {
if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) { if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) {
mSuggestionLoaded = false;
return new SuggestionLoader(mContext, mSuggestionController); return new SuggestionLoader(mContext, mSuggestionController);
} }
throw new IllegalArgumentException("This loader id is not supported " + id); throw new IllegalArgumentException("This loader id is not supported " + id);
@@ -113,12 +116,17 @@ public class SuggestionControllerMixin implements SuggestionController.ServiceCo
@Override @Override
public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) { public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) {
mSuggestionLoaded = true;
mHost.onSuggestionReady(data); mHost.onSuggestionReady(data);
} }
@Override @Override
public void onLoaderReset(Loader<List<Suggestion>> loader) { public void onLoaderReset(Loader<List<Suggestion>> loader) {
mSuggestionLoaded = false;
}
public boolean isSuggestionLoaded() {
return mSuggestionLoaded;
} }
public void dismissSuggestion(Suggestion suggestion) { public void dismissSuggestion(Suggestion suggestion) {

View File

@@ -18,9 +18,7 @@ package com.android.settings.dashboard.suggestions;
import static android.arch.lifecycle.Lifecycle.Event.ON_START; import static android.arch.lifecycle.Lifecycle.Event.ON_START;
import static android.arch.lifecycle.Lifecycle.Event.ON_STOP; import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -37,9 +35,9 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@@ -49,19 +47,18 @@ import org.robolectric.annotation.Config;
}) })
public class SuggestionControllerMixinTest { public class SuggestionControllerMixinTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock @Mock
private SuggestionControllerMixin.SuggestionControllerHost mHost; private SuggestionControllerMixin.SuggestionControllerHost mHost;
private Context mContext;
private Lifecycle mLifecycle; private Lifecycle mLifecycle;
private SuggestionControllerMixin mMixin; private SuggestionControllerMixin mMixin;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mContext); mContext = RuntimeEnvironment.application;
FakeFeatureFactory.setupForTest();
mLifecycle = new Lifecycle(() -> mLifecycle); mLifecycle = new Lifecycle(() -> mLifecycle);
when(mContext.getApplicationContext()).thenReturn(mContext);
} }
@After @After
@@ -111,4 +108,17 @@ public class SuggestionControllerMixinTest {
verify(mHost).getLoaderManager(); 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();
}
} }