Merge "Safeguard against no tiles in QS" into qt-dev
am: 60aabc40a2

Change-Id: If5bde9600167a3d508bef5377d31e6d8db2635c2
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index e22a21a..991d9fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -244,7 +244,9 @@
 
     private void emptyAndInflateOrRemovePages() {
         final int nTiles = mTiles.size();
-        int numPages = nTiles / mPages.get(0).maxTiles();
+        // We should always have at least one page, even if it's empty.
+        int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
+
         // Add one more not full page if needed
         numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
 
@@ -434,11 +436,14 @@
         }
 
         public boolean isFull() {
-            return mRecords.size() >= mColumns * mRows;
+            return mRecords.size() >= maxTiles();
         }
 
         public int maxTiles() {
-            return mColumns * mRows;
+            // Each page should be able to hold at least one tile. If there's not enough room to
+            // show even 1 or there are no tiles, it probably means we are in the middle of setting
+            // up.
+            return Math.max(mColumns * mRows, 1);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index af7de0e6..fed59a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -261,12 +261,19 @@
             }
         }
         mCurrentUser = currentUser;
+        List<String> currentSpecs = new ArrayList(mTileSpecs);
         mTileSpecs.clear();
         mTileSpecs.addAll(tileSpecs);
         mTiles.clear();
         mTiles.putAll(newTiles);
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onTilesChanged();
+        if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
+            // If we didn't manage to create any tiles, set it to empty (default)
+            if (DEBUG) Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
+            changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
+        } else {
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                mCallbacks.get(i).onTilesChanged();
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index f73472f..f2292fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -18,23 +18,28 @@
 
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertFalse;
 
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.util.CollectionUtils;
 import com.android.systemui.DumpController;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
@@ -108,7 +113,6 @@
                             return null;
                     }
                 });
-
     }
 
     @Test
@@ -124,6 +128,26 @@
     }
 
     @Test
+    public void testInvalidSpecUsesDefault() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "not-valid");
+
+        assertEquals(2, mQSTileHost.getTiles().size());
+    }
+
+    @Test
+    public void testSpecWithInvalidDoesNotUseDefault() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec2,not-valid");
+
+        assertEquals(1, mQSTileHost.getTiles().size());
+        QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
+        assertTrue(element instanceof TestTile2);
+    }
+
+    @Test
     public void testDump() {
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
         StringWriter w = new StringWriter();
@@ -153,6 +177,21 @@
         @Override
         public void onPluginDisconnected(QSFactory plugin) {
         }
+
+        @Override
+        public void changeTiles(List<String> previousTiles, List<String> newTiles) {
+            String previousSetting = Settings.Secure.getStringForUser(
+                    getContext().getContentResolver(), TILES_SETTING,
+                    ActivityManager.getCurrentUser());
+            super.changeTiles(previousTiles, newTiles);
+            // After tiles are changed, make sure to call onTuningChanged with the new setting if it
+            // changed
+            String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(),
+                    TILES_SETTING, ActivityManager.getCurrentUser());
+            if (!previousSetting.equals(newSetting)) {
+                onTuningChanged(TILES_SETTING, newSetting);
+            }
+        }
     }
 
     private class TestTile extends QSTileImpl<QSTile.State> {