Reduce flickers of Injection

The injection dynamic data was loaded in the background and then post to
main thread to update UI. However, it usually updates after
Fragement.onResume(), which causes the flicker.

To make it more smooth, DashboardFragment to wait for the dynamic data
observers to update UI for a short period, which eliminates the flicker
in most cases.

Also skip the repeated tiles refresh called by onCategoriesChanged in
onResume after all preferences refreshed.

Test: robotest, visual
Bug: 229177114
Change-Id: I04650af9692703f1fc1e6e5ad2090f051b1eeb81
This commit is contained in:
Jason Chiu
2022-05-06 11:14:54 +08:00
parent 9a14f087cd
commit c9615611e1
7 changed files with 103 additions and 27 deletions

View File

@@ -16,7 +16,6 @@
package com.android.settings.dashboard;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
@@ -57,6 +56,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Base fragment for dashboard style UI containing a list of static and dynamic setting items.
@@ -66,6 +67,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
BasePreferenceController.UiBlockListener {
public static final String CATEGORY = "category";
private static final String TAG = "DashboardFragment";
private static final long TIMEOUT_MILLIS = 50L;
@VisibleForTesting
final ArrayMap<String, List<DynamicDataObserver>> mDashboardTilePrefKeys = new ArrayMap<>();
@@ -461,8 +463,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
// Create a list to track which tiles are to be removed.
final Map<String, List<DynamicDataObserver>> remove = new ArrayMap(mDashboardTilePrefKeys);
// Install dashboard tiles.
// Install dashboard tiles and collect pending observers.
final boolean forceRoundedIcons = shouldForceRoundedIcon();
final List<DynamicDataObserver> pendingObservers = new ArrayList<>();
for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) {
@@ -472,26 +475,30 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
if (!displayTile(tile)) {
continue;
}
final List<DynamicDataObserver> observers;
if (mDashboardTilePrefKeys.containsKey(key)) {
// Have the key already, will rebind.
final Preference preference = screen.findPreference(key);
mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this,
forceRoundedIcons, preference, tile, key,
observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
getActivity(), this, forceRoundedIcons, preference, tile, key,
mPlaceholderPreferenceController.getOrder());
} else {
// Don't have this key, add it.
final Preference pref = createPreference(tile);
final List<DynamicDataObserver> observers =
mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
this, forceRoundedIcons, pref, tile, key,
mPlaceholderPreferenceController.getOrder());
observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
getActivity(), this, forceRoundedIcons, pref, tile, key,
mPlaceholderPreferenceController.getOrder());
screen.addPreference(pref);
registerDynamicDataObservers(observers);
mDashboardTilePrefKeys.put(key, observers);
}
if (observers != null) {
pendingObservers.addAll(observers);
}
remove.remove(key);
}
// Finally remove tiles that are gone.
// Remove tiles that are gone.
for (Map.Entry<String, List<DynamicDataObserver>> entry : remove.entrySet()) {
final String key = entry.getKey();
mDashboardTilePrefKeys.remove(key);
@@ -501,6 +508,20 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
}
unregisterDynamicDataObservers(entry.getValue());
}
// Wait for pending observers to update UI.
if (!pendingObservers.isEmpty()) {
final CountDownLatch mainLatch = new CountDownLatch(1);
new Thread(() -> {
pendingObservers.forEach(observer ->
awaitObserverLatch(observer.getCountDownLatch()));
mainLatch.countDown();
}).start();
Log.d(tag, "Start waiting observers");
awaitObserverLatch(mainLatch);
Log.d(tag, "Stop waiting observers");
pendingObservers.forEach(DynamicDataObserver::updateUi);
}
}
@Override
@@ -546,4 +567,12 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
resolver.unregisterContentObserver(observer);
});
}
private void awaitObserverLatch(CountDownLatch latch) {
try {
latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// Do nothing
}
}
}