diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index e22a21ad1fedf..991d9fa549ac4 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 class PagedTileLayout extends ViewPager implements QSTileLayout { } 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 af7de0e6b234e..fed59a526bab6 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 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D } } mCurrentUser = currentUser; + List 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 f73472f86d8c6..f2292fd96b8da 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 @@ package com.android.systemui.qs; 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 @@ public class QSTileHostTest extends SysuiTestCase { return null; } }); - } @Test @@ -123,6 +127,26 @@ public class QSTileHostTest extends SysuiTestCase { assertFalse(tiles.isEmpty()); } + @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"); @@ -153,6 +177,21 @@ public class QSTileHostTest extends SysuiTestCase { @Override public void onPluginDisconnected(QSFactory plugin) { } + + @Override + public void changeTiles(List previousTiles, List 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 {