Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ca203c2 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Fix refresh tile when tile is already listening

Calling refreshTile externally on a tile that is already listening can
have unexpected side effects, for example if the tile is tracking a user
interaction (e.g. turning on bt).

In particular, QSPanelController should not call it for tiles that are already
listening. This is the case for tiles that are also in QQS.

Test: manual, toggle bt ON in QQS and expand quickly.
Test: atest com.android.systemui.qs
Fixes: 225404999
Change-Id: Id0559ad95ecf6a28475abbdd8b989c745badd0b8
parent 1283caac
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ import java.util.function.Supplier;
@DependsOn(target = Icon.class)
@DependsOn(target = State.class)
public interface QSTile {
    int VERSION = 3;
    int VERSION = 4;

    String getTileSpec();

@@ -114,6 +114,12 @@ public interface QSTile {
        return false;
    }

    /**
     * Return whether the tile is set to its listening state and therefore receiving updates and
     * refreshes from controllers
     */
    boolean isListening();

    @ProvidesInterface(version = Callback.VERSION)
    interface Callback {
        static final int VERSION = 2;
+6 −1
Original line number Diff line number Diff line
@@ -217,9 +217,14 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
    /** */
    public void refreshAllTiles() {
        for (QSPanelControllerBase.TileRecord r : mRecords) {
            if (!r.tile.isListening()) {
                // Only refresh tiles that were not already in the listening state. Tiles that are
                // already listening is as if they are already expanded (for example, tiles that
                // are both in QQS and QS).
                r.tile.refreshState();
            }
        }
    }

    private void addTile(final QSTile tile, boolean collapsedView) {
        final TileRecord r =
+6 −1
Original line number Diff line number Diff line
@@ -331,6 +331,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
        refreshState(null);
    }

    @Override
    public final boolean isListening() {
        return getLifecycle().getCurrentState().isAtLeast(RESUMED);
    }

    protected final void refreshState(@Nullable Object arg) {
        mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
    }
@@ -416,7 +421,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
    @Nullable
    public abstract Intent getLongClickIntent();

    protected void handleRefreshState(@Nullable Object arg) {
    protected final void handleRefreshState(@Nullable Object arg) {
        handleUpdateState(mTmpState, arg);
        boolean changed = mTmpState.copyTo(mState);
        if (mReadyState == READY_STATE_READYING) {
+17 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
@@ -62,6 +63,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.List;

@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -89,6 +91,8 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
    @Mock
    QSTileImpl mQSTile;
    @Mock
    QSTile mOtherTile;
    @Mock
    QSTileView mQSTileView;
    @Mock
    PagedTileLayout mPagedTileLayout;
@@ -280,4 +284,17 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
        assertThat(mController.shouldUseHorizontalLayout()).isFalse();
        verify(mHorizontalLayoutListener, times(2)).run();
    }

    @Test
    public void testRefreshAllTilesDoesntRefreshListeningTiles() {
        when(mQSTileHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
        mController.setTiles();

        when(mQSTile.isListening()).thenReturn(false);
        when(mOtherTile.isListening()).thenReturn(true);

        mController.refreshAllTiles();
        verify(mQSTile).refreshState();
        verify(mOtherTile, never()).refreshState();
    }
}
+17 −5
Original line number Diff line number Diff line
@@ -4,13 +4,13 @@ import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.settings.brightness.BrightnessController
@@ -21,11 +21,12 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever

@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -49,6 +50,8 @@ class QSPanelControllerTest : SysuiTestCase() {
    @Mock private lateinit var falsingManager: FalsingManager
    @Mock private lateinit var featureFlags: FeatureFlags
    @Mock private lateinit var mediaHost: MediaHost
    @Mock private lateinit var tile: QSTile
    @Mock private lateinit var otherTile: QSTile

    private lateinit var controller: QSPanelController

@@ -93,8 +96,17 @@ class QSPanelControllerTest : SysuiTestCase() {
        verify(mediaHost).expansion = MediaHostState.EXPANDED
    }

    private fun setSplitShadeEnabled(enabled: Boolean) {
        mContext.orCreateTestableResources
            .addOverride(R.bool.config_use_split_notification_shade, enabled)
    @Test
    fun testSetListeningDoesntRefreshListeningTiles() {
        whenever(qsTileHost.getTiles()).thenReturn(listOf(tile, otherTile))
        controller.setTiles()
        whenever(tile.isListening()).thenReturn(false)
        whenever(otherTile.isListening()).thenReturn(true)
        whenever(qsPanel.isListening).thenReturn(true)

        controller.setListening(true, true)

        verify(tile).refreshState()
        verify(otherTile, Mockito.never()).refreshState()
    }
}
Loading