Reduce the flickering of injected items when package is changed am: e4b2b77452

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/12771880

Change-Id: Ib2ff68b59e8aa9567d1f6f7c7281832dd323c2dc
This commit is contained in:
Jason Chiu
2020-10-12 04:31:57 +00:00
committed by Automerger Merge Worker
3 changed files with 126 additions and 20 deletions

View File

@@ -41,11 +41,14 @@ import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.dashboard.CategoryManager;
import com.android.settingslib.drawer.Tile;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SettingsBaseActivity extends FragmentActivity {
@@ -59,6 +62,7 @@ public class SettingsBaseActivity extends FragmentActivity {
private final PackageReceiver mPackageReceiver = new PackageReceiver();
private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
private int mCategoriesUpdateTaskCount;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -147,10 +151,10 @@ public class SettingsBaseActivity extends FragmentActivity {
((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
}
private void onCategoriesChanged() {
private void onCategoriesChanged(Set<String> categories) {
final int N = mCategoryListeners.size();
for (int i = 0; i < N; i++) {
mCategoryListeners.get(i).onCategoriesChanged();
mCategoryListeners.get(i).onCategoriesChanged(categories);
}
}
@@ -194,38 +198,100 @@ public class SettingsBaseActivity extends FragmentActivity {
* Updates dashboard categories. Only necessary to call this after setTileEnabled
*/
public void updateCategories() {
new CategoriesUpdateTask().execute();
updateCategories(false /* fromBroadcast */);
}
private void updateCategories(boolean fromBroadcast) {
// Only allow at most 2 tasks existing at the same time since when the first one is
// executing, there may be new data from the second update request.
// Ignore the third update request because the second task is still waiting for the first
// task to complete in a serial thread, which will get the latest data.
if (mCategoriesUpdateTaskCount < 2) {
new CategoriesUpdateTask().execute(fromBroadcast);
}
}
public interface CategoryListener {
void onCategoriesChanged();
/**
* @param categories the changed categories that have to be refreshed, or null to force
* refreshing all.
*/
void onCategoriesChanged(@Nullable Set<String> categories);
}
private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {
private class CategoriesUpdateTask extends AsyncTask<Boolean, Void, Set<String>> {
private final Context mContext;
private final CategoryManager mCategoryManager;
private Map<ComponentName, Tile> mPreviousTileMap;
public CategoriesUpdateTask() {
mCategoryManager = CategoryManager.get(SettingsBaseActivity.this);
mCategoriesUpdateTaskCount++;
mContext = SettingsBaseActivity.this;
mCategoryManager = CategoryManager.get(mContext);
}
@Override
protected Void doInBackground(Void... params) {
mCategoryManager.reloadAllCategories(SettingsBaseActivity.this);
return null;
}
@Override
protected void onPostExecute(Void result) {
protected Set<String> doInBackground(Boolean... params) {
mPreviousTileMap = mCategoryManager.getTileByComponentMap();
mCategoryManager.reloadAllCategories(mContext);
mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
onCategoriesChanged();
return getChangedCategories(params[0]);
}
@Override
protected void onPostExecute(Set<String> categories) {
if (categories == null || !categories.isEmpty()) {
onCategoriesChanged(categories);
}
mCategoriesUpdateTaskCount--;
}
// Return the changed categories that have to be refreshed, or null to force refreshing all.
private Set<String> getChangedCategories(boolean fromBroadcast) {
if (!fromBroadcast) {
// Always refresh for non-broadcast case.
return null;
}
final Set<String> changedCategories = new ArraySet<>();
final Map<ComponentName, Tile> currentTileMap =
mCategoryManager.getTileByComponentMap();
currentTileMap.forEach((component, currentTile) -> {
final Tile previousTile = mPreviousTileMap.get(component);
// Check if the tile is newly added.
if (previousTile == null) {
Log.i(TAG, "Tile added: " + component.flattenToShortString());
changedCategories.add(currentTile.getCategory());
return;
}
// Check if the title or summary has changed.
if (!TextUtils.equals(currentTile.getTitle(mContext),
previousTile.getTitle(mContext))
|| !TextUtils.equals(currentTile.getSummary(mContext),
previousTile.getSummary(mContext))) {
Log.i(TAG, "Tile changed: " + component.flattenToShortString());
changedCategories.add(currentTile.getCategory());
}
});
// Check if any previous tile is removed.
final Set<ComponentName> removal = new ArraySet(mPreviousTileMap.keySet());
removal.removeAll(currentTileMap.keySet());
removal.forEach(component -> {
Log.i(TAG, "Tile removed: " + component.flattenToShortString());
changedCategories.add(mPreviousTileMap.get(component).getCategory());
});
return changedCategories;
}
}
private class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
updateCategories();
updateCategories(true /* fromBroadcast */);
}
}
}

View File

@@ -41,6 +41,7 @@ import java.util.Set;
public class CategoryManager {
private static final String TAG = "CategoryManager";
private static final boolean DEBUG = false;
private static CategoryManager sInstance;
private final InterestingConfigChanges mInterestingConfigChanges;
@@ -88,6 +89,7 @@ public class CategoryManager {
public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
if (mCategories == null) {
Log.w(TAG, "Category is null, skipping blacklist update");
return;
}
for (int i = 0; i < mCategories.size(); i++) {
DashboardCategory category = mCategories.get(i);
@@ -100,6 +102,31 @@ public class CategoryManager {
}
}
/** Return the current tile map */
public synchronized Map<ComponentName, Tile> getTileByComponentMap() {
final Map<ComponentName, Tile> result = new ArrayMap<>();
if (mCategories == null) {
Log.w(TAG, "Category is null, no tiles");
return result;
}
mCategories.forEach(category -> {
for (int i = 0; i < category.getTilesCount(); i++) {
final Tile tile = category.getTile(i);
result.put(tile.getIntent().getComponent(), tile);
}
});
return result;
}
private void logTiles(Context context) {
if (DEBUG) {
getTileByComponentMap().forEach((component, tile) -> {
Log.d(TAG, "Tile: " + tile.getCategory().replace("com.android.settings.", "")
+ ": " + tile.getTitle(context) + ", " + component.flattenToShortString());
});
}
}
private synchronized void tryInitCategories(Context context) {
// Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
// happens.
@@ -108,6 +135,7 @@ public class CategoryManager {
private synchronized void tryInitCategories(Context context, boolean forceClearCache) {
if (mCategories == null) {
final boolean firstLoading = mCategoryByKeyMap.isEmpty();
if (forceClearCache) {
mTileByComponentCache.clear();
}
@@ -119,6 +147,9 @@ public class CategoryManager {
backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
sortCategories(context, mCategoryByKeyMap);
filterDuplicateTiles(mCategoryByKeyMap);
if (firstLoading) {
logTiles(context);
}
}
}

View File

@@ -56,6 +56,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
@@ -160,13 +161,21 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
}
@Override
public void onCategoriesChanged() {
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
public void onCategoriesChanged(Set<String> categories) {
final String categoryKey = getCategoryKey();
final DashboardCategory dashboardCategory =
mDashboardFeatureProvider.getTilesForCategory(categoryKey);
if (dashboardCategory == null) {
return;
}
refreshDashboardTiles(getLogTag());
if (categories == null) {
// force refreshing
refreshDashboardTiles(getLogTag());
} else if (categories.contains(categoryKey)) {
Log.i(TAG, "refresh tiles for " + categoryKey);
refreshDashboardTiles(getLogTag());
}
}
@Override