From fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03a Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 14 Nov 2016 09:48:06 -0800 Subject: [PATCH] Group injected tiles together based on package name. During grouping, tiles are sorted based on package name, and their own priority value. However if the package is Settings itself, the ordering is not changed. Before sort: [pkg1, 100], [pgk2, 120], [pkg1, 130] After sort: [pkg1, 1], [pkg1, 2], [pkg2,3] This is necessary to make sure settings app have the overall control of ordering of all dynamic tiles. When each app define their priority, they don't have access to the global vision of what other settings are on screen so it's possible different app define conflicting orderings. Settings app is the only reasonable place to rank them properly. Bug: 32827787 Test: RunSettingsLibRoboTests Change-Id: I38de55530e61da9de7532ef6a7ee97ef89aca9d9 --- .../settingslib/drawer/CategoryManager.java | 58 +++++++++ .../drawer/CategoryManagerTest.java | 112 ++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java index ed411be9c3e4d..14a0e820a69a3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java @@ -18,6 +18,7 @@ package com.android.settingslib.drawer; import android.content.ComponentName; import android.content.Context; import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; @@ -25,12 +26,15 @@ import android.util.Pair; import com.android.settingslib.applications.InterestingConfigChanges; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import static java.lang.String.CASE_INSENSITIVE_ORDER; + public class CategoryManager { private static final String TAG = "CategoryManager"; @@ -111,6 +115,7 @@ public class CategoryManager { mCategoryByKeyMap.put(category.key, category); } backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap); + normalizePriority(context, mCategoryByKeyMap); } } @@ -163,4 +168,57 @@ public class CategoryManager { } } } + + /** + * Normalize priority values on tiles across injected from all apps to make sure they don't set + * the same priority value. However internal tiles' priority remains unchanged. + *

+ * A list of tiles are considered normalized when their priority value increases in a linear + * scan. + */ + @VisibleForTesting + synchronized void normalizePriority(Context context, + Map categoryByKeyMap) { + for (Entry categoryEntry : categoryByKeyMap.entrySet()) { + normalizePriorityForExternalTiles(context, categoryEntry.getValue()); + } + } + + /** + * Normalize priority value for tiles within a single {@code DashboardCategory}. + * + * @see #normalizePriority(Context, Map) + */ + private synchronized void normalizePriorityForExternalTiles(Context context, + DashboardCategory dashboardCategory) { + final String skipPackageName = context.getPackageName(); + + // Sort tiles based on [package, priority within package] + Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> { + final String package1 = tile1.intent.getComponent().getPackageName(); + final String package2 = tile2.intent.getComponent().getPackageName(); + final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2); + // First sort by package name + if (packageCompare != 0) { + return packageCompare; + } else if (TextUtils.equals(package1, skipPackageName)) { + return 0; + } + // Then sort by priority + return tile1.priority - tile2.priority; + }); + // Update priority for all items so no package define the same priority value. + final int count = dashboardCategory.tiles.size(); + for (int i = 0; i < count; i++) { + final String packageName = + dashboardCategory.tiles.get(i).intent.getComponent().getPackageName(); + if (TextUtils.equals(packageName, skipPackageName)) { + // We skip this tile because it's a intent pointing to our own app. We trust the + // priority is set correctly, so don't normalize. + continue; + } + dashboardCategory.tiles.get(i).priority = i; + + } + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java index 380f6226512af..50bb216c9d39e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java @@ -16,7 +16,9 @@ package com.android.settingslib.drawer; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.util.Pair; import com.android.settingslib.TestConfig; @@ -116,4 +118,114 @@ public class CategoryManagerTest { // Old category still exists. assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1); } + + @Test + public void normalizePriority_singlePackage_shouldReorderBasedOnPriority() { + // Create some fake tiles that are not sorted. + final String testPackage = "com.android.test"; + final DashboardCategory category = new DashboardCategory(); + final Tile tile1 = new Tile(); + tile1.intent = + new Intent().setComponent(new ComponentName(testPackage, "class1")); + tile1.priority = 100; + final Tile tile2 = new Tile(); + tile2.intent = + new Intent().setComponent(new ComponentName(testPackage, "class2")); + tile2.priority = 50; + final Tile tile3 = new Tile(); + tile3.intent = + new Intent().setComponent(new ComponentName(testPackage, "class3")); + tile3.priority = 200; + category.tiles.add(tile1); + category.tiles.add(tile2); + category.tiles.add(tile3); + mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); + + // Normalize their priorities + mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(), + mCategoryByKeyMap); + + // Verify they are now sorted. + assertThat(category.tiles.get(0)).isSameAs(tile2); + assertThat(category.tiles.get(1)).isSameAs(tile1); + assertThat(category.tiles.get(2)).isSameAs(tile3); + // Verify their priority is normalized + assertThat(category.tiles.get(0).priority).isEqualTo(0); + assertThat(category.tiles.get(1).priority).isEqualTo(1); + assertThat(category.tiles.get(2).priority).isEqualTo(2); + } + + @Test + public void normalizePriority_multiPackage_shouldReorderBasedOnPackageAndPriority() { + // Create some fake tiles that are not sorted. + final String testPackage1 = "com.android.test1"; + final String testPackage2 = "com.android.test2"; + final DashboardCategory category = new DashboardCategory(); + final Tile tile1 = new Tile(); + tile1.intent = + new Intent().setComponent(new ComponentName(testPackage2, "class1")); + tile1.priority = 100; + final Tile tile2 = new Tile(); + tile2.intent = + new Intent().setComponent(new ComponentName(testPackage1, "class2")); + tile2.priority = 100; + final Tile tile3 = new Tile(); + tile3.intent = + new Intent().setComponent(new ComponentName(testPackage1, "class3")); + tile3.priority = 50; + category.tiles.add(tile1); + category.tiles.add(tile2); + category.tiles.add(tile3); + mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); + + // Normalize their priorities + mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(), + mCategoryByKeyMap); + + // Verify they are now sorted. + assertThat(category.tiles.get(0)).isSameAs(tile3); + assertThat(category.tiles.get(1)).isSameAs(tile2); + assertThat(category.tiles.get(2)).isSameAs(tile1); + // Verify their priority is normalized + assertThat(category.tiles.get(0).priority).isEqualTo(0); + assertThat(category.tiles.get(1).priority).isEqualTo(1); + assertThat(category.tiles.get(2).priority).isEqualTo(2); + } + + @Test + public void normalizePriority_internalPackageTiles_shouldSkipTileForInternalPackage() { + // Create some fake tiles that are not sorted. + final String testPackage = + ShadowApplication.getInstance().getApplicationContext().getPackageName(); + final DashboardCategory category = new DashboardCategory(); + final Tile tile1 = new Tile(); + tile1.intent = + new Intent().setComponent(new ComponentName(testPackage, "class1")); + tile1.priority = 100; + final Tile tile2 = new Tile(); + tile2.intent = + new Intent().setComponent(new ComponentName(testPackage, "class2")); + tile2.priority = 100; + final Tile tile3 = new Tile(); + tile3.intent = + new Intent().setComponent(new ComponentName(testPackage, "class3")); + tile3.priority = 50; + category.tiles.add(tile1); + category.tiles.add(tile2); + category.tiles.add(tile3); + mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); + + // Normalize their priorities + mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(), + mCategoryByKeyMap); + + // Verify the sorting order is not changed + assertThat(category.tiles.get(0)).isSameAs(tile1); + assertThat(category.tiles.get(1)).isSameAs(tile2); + assertThat(category.tiles.get(2)).isSameAs(tile3); + // Verify their priorities are not changed. + assertThat(category.tiles.get(0).priority).isEqualTo(100); + assertThat(category.tiles.get(1).priority).isEqualTo(100); + assertThat(category.tiles.get(2).priority).isEqualTo(50); + } }