Merge "Add PanelInteractor" into udc-dev am: e32211c151

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22117876

Change-Id: If9369a7724247b5d0fd7b14117bfc578c0bf0963
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Fabian Kozynski
2023-03-16 17:14:19 +00:00
committed by Automerger Merge Worker
12 changed files with 276 additions and 26 deletions

View File

@@ -26,6 +26,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
@@ -33,7 +34,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public interface QSHost {
public interface QSHost extends PanelInteractor {
String TILES_SETTING = Settings.Secure.QS_TILES;
int POSITION_AT_END = -1;
@@ -57,9 +58,6 @@ public interface QSHost {
}
void warn(String message, Throwable t);
void collapsePanels();
void forceCollapsePanels();
void openPanels();
Context getContext();
Context getUserContext();
int getUserId();

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.qs.dagger
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QSTileHost
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@Module
interface QSHostModule {
@Binds fun provideQsHost(controllerImpl: QSTileHost): QSHost
@Module
companion object {
@Provides
@JvmStatic
fun providePanelInteractor(
featureFlags: FeatureFlags,
qsHost: QSHost,
panelInteractorImpl: PanelInteractorImpl
): PanelInteractor {
return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
panelInteractorImpl
} else {
qsHost
}
}
}
}

View File

@@ -27,7 +27,6 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -45,7 +44,6 @@ import java.util.Map;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
@@ -54,7 +52,13 @@ import dagger.multibindings.Multibinds;
* Module for QS dependencies
*/
@Module(subcomponents = {QSFragmentComponent.class},
includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class})
includes = {
MediaModule.class,
QSExternalModule.class,
QSFlagsModule.class,
QSHostModule.class
}
)
public interface QSModule {
/** A map of internal QS tiles. Ensures that this can be injected even if
@@ -100,8 +104,4 @@ public interface QSModule {
manager.init();
return manager;
}
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);
}

View File

@@ -40,6 +40,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -74,6 +75,7 @@ public class TileServices extends IQSService.Stub {
private final CommandQueue mCommandQueue;
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
private final PanelInteractor mPanelInteractor;
private int mMaxBound = DEFAULT_MAX_BOUND;
@@ -85,7 +87,8 @@ public class TileServices extends IQSService.Stub {
UserTracker userTracker,
KeyguardStateController keyguardStateController,
CommandQueue commandQueue,
StatusBarIconController statusBarIconController) {
StatusBarIconController statusBarIconController,
PanelInteractor panelInteractor) {
mHost = host;
mKeyguardStateController = keyguardStateController;
mContext = mHost.getContext();
@@ -96,6 +99,7 @@ public class TileServices extends IQSService.Stub {
mCommandQueue = commandQueue;
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
mPanelInteractor = panelInteractor;
}
public Context getContext() {
@@ -255,7 +259,7 @@ public class TileServices extends IQSService.Stub {
if (customTile != null) {
verifyCaller(customTile);
customTile.onDialogShown();
mHost.forceCollapsePanels();
mPanelInteractor.forceCollapsePanels();
Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(true);
}
}
@@ -275,7 +279,7 @@ public class TileServices extends IQSService.Stub {
CustomTile customTile = getTileForToken(token);
if (customTile != null) {
verifyCaller(customTile);
mHost.forceCollapsePanels();
mPanelInteractor.forceCollapsePanels();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.qs.pipeline.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.CentralSurfaces
import java.util.Optional
import javax.inject.Inject
/** Encapsulates business logic for interacting with the QS panel. */
interface PanelInteractor {
/** Collapse the shade */
fun collapsePanels()
/** Collapse the shade forcefully, skipping some animations. */
fun forceCollapsePanels()
/** Open the Quick Settings panel */
fun openPanels()
}
@SysUISingleton
class PanelInteractorImpl
@Inject
constructor(
private val centralSurfaces: Optional<CentralSurfaces>,
) : PanelInteractor {
override fun collapsePanels() {
centralSurfaces.ifPresent { it.postAnimateCollapsePanels() }
}
override fun forceCollapsePanels() {
centralSurfaces.ifPresent { it.postAnimateForceCollapsePanels() }
}
override fun openPanels() {
centralSurfaces.ifPresent { it.postAnimateOpenPanels() }
}
}

View File

@@ -38,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -52,6 +53,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
private final LocationController mController;
private final KeyguardStateController mKeyguard;
private final PanelInteractor mPanelInteractor;
private final Callback mCallback = new Callback();
@Inject
@@ -65,12 +67,14 @@ public class LocationTile extends QSTileImpl<BooleanState> {
ActivityStarter activityStarter,
QSLogger qsLogger,
LocationController locationController,
KeyguardStateController keyguardStateController
KeyguardStateController keyguardStateController,
PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
mPanelInteractor = panelInteractor;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
}
@@ -90,7 +94,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
final boolean wasEnabled = mState.value;
mHost.openPanels();
mPanelInteractor.openPanels();
mController.setLocationEnabled(!wasEnabled);
});
return;

View File

@@ -42,6 +42,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -66,6 +67,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
private final Callback mCallback = new Callback();
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final FeatureFlags mFlags;
private final PanelInteractor mPanelInteractor;
private long mMillisUntilFinished = 0;
@@ -83,7 +85,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
RecordingController controller,
KeyguardDismissUtil keyguardDismissUtil,
KeyguardStateController keyguardStateController,
DialogLaunchAnimator dialogLaunchAnimator
DialogLaunchAnimator dialogLaunchAnimator,
PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -93,6 +96,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
mKeyguardDismissUtil = keyguardDismissUtil;
mKeyguardStateController = keyguardStateController;
mDialogLaunchAnimator = dialogLaunchAnimator;
mPanelInteractor = panelInteractor;
}
@Override
@@ -171,7 +175,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
// disable the exit animation which looks weird when it happens at the same time as the
// shade collapsing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
getHost().collapsePanels();
mPanelInteractor.collapsePanels();
};
final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,

View File

@@ -59,6 +59,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -412,6 +413,7 @@ public class GarbageMonitor implements Dumpable {
private final GarbageMonitor gm;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
private final PanelInteractor mPanelInteractor;
@Inject
public MemoryTile(
@@ -423,11 +425,13 @@ public class GarbageMonitor implements Dumpable {
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
GarbageMonitor monitor
GarbageMonitor monitor,
PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
gm = monitor;
mPanelInteractor = panelInteractor;
}
@Override
@@ -457,7 +461,7 @@ public class GarbageMonitor implements Dumpable {
mHandler.post(() -> {
dumpInProgress = false;
refreshState();
getHost().collapsePanels();
mPanelInteractor.collapsePanels();
mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0);
});
}

View File

@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -41,6 +42,7 @@ import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -91,6 +93,8 @@ public class TileServicesTest extends SysuiTestCase {
private TileLifecycleManager mTileLifecycleManager;
@Mock
private QSHost mQSHost;
@Mock
private PanelInteractor mPanelInteractor;
@Before
public void setUp() throws Exception {
@@ -107,7 +111,8 @@ public class TileServicesTest extends SysuiTestCase {
Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper());
mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController);
mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController,
mPanelInteractor);
}
@After
@@ -222,13 +227,37 @@ public class TileServicesTest extends SysuiTestCase {
verify(tile, never()).startActivityAndCollapse(pi);
}
@Test
public void testOnStartActivityCollapsesPanel() {
CustomTile tile = mock(CustomTile.class);
ComponentName componentName = mock(ComponentName.class);
when(tile.getComponent()).thenReturn(componentName);
when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName());
TileServiceManager manager = mTileService.getTileWrapper(tile);
mTileService.onStartActivity(manager.getToken());
verify(mPanelInteractor).forceCollapsePanels();
}
@Test
public void testOnShowDialogCollapsesPanel() {
CustomTile tile = mock(CustomTile.class);
ComponentName componentName = mock(ComponentName.class);
when(tile.getComponent()).thenReturn(componentName);
when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName());
TileServiceManager manager = mTileService.getTileWrapper(tile);
mTileService.onShowDialog(manager.getToken());
verify(mPanelInteractor).forceCollapsePanels();
}
private class TestTileServices extends TileServices {
TestTileServices(QSHost host, Provider<Handler> handlerProvider,
BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
KeyguardStateController keyguardStateController, CommandQueue commandQueue,
StatusBarIconController statusBarIconController) {
StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
commandQueue, statusBarIconController);
commandQueue, statusBarIconController, panelInteractor);
}
@Override
@@ -237,6 +266,8 @@ public class TileServicesTest extends SysuiTestCase {
TileServiceManager manager = mock(TileServiceManager.class);
mManagers.add(manager);
when(manager.isLifecycleStarted()).thenReturn(true);
Binder b = new Binder();
when(manager.getToken()).thenReturn(b);
return manager;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.qs.pipeline.domain.interactor
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.CentralSurfaces
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class PanelInteractorImplTest : SysuiTestCase() {
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
}
@Test
fun openPanels_callsCentralSurfaces() {
val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
underTest.openPanels()
verify(centralSurfaces).postAnimateOpenPanels()
}
@Test
fun collapsePanels_callsCentralSurfaces() {
val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
underTest.collapsePanels()
verify(centralSurfaces).postAnimateCollapsePanels()
}
@Test
fun forceCollapsePanels_callsCentralSurfaces() {
val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
underTest.forceCollapsePanels()
verify(centralSurfaces).postAnimateForceCollapsePanels()
}
@Test
fun whenOptionalEmpty_doesnThrow() {
val underTest = PanelInteractorImpl(Optional.empty())
underTest.openPanels()
underTest.collapsePanels()
underTest.forceCollapsePanels()
}
}

View File

@@ -31,9 +31,12 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -41,6 +44,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -65,6 +69,8 @@ class LocationTileTest : SysuiTestCase() {
private lateinit var locationController: LocationController
@Mock
private lateinit var keyguardStateController: KeyguardStateController
@Mock
private lateinit var panelInteractor: PanelInteractor
private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
@@ -86,7 +92,9 @@ class LocationTileTest : SysuiTestCase() {
activityStarter,
qsLogger,
locationController,
keyguardStateController)
keyguardStateController,
panelInteractor,
)
}
@After
@@ -116,4 +124,18 @@ class LocationTileTest : SysuiTestCase() {
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on))
}
@Test
fun testClickWhenLockedWillCallOpenPanels() {
`when`(keyguardStateController.isMethodSecure).thenReturn(true)
`when`(keyguardStateController.isShowing).thenReturn(true)
tile.handleClick(null)
val captor = argumentCaptor<Runnable>()
verify(activityStarter).postQSRunnableDismissingKeyguard(capture(captor))
captor.value.run()
verify(panelInteractor).openPanels()
}
}

View File

@@ -45,6 +45,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -83,6 +84,8 @@ public class ScreenRecordTileTest extends SysuiTestCase {
private KeyguardStateController mKeyguardStateController;
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
@Mock
private PanelInteractor mPanelInteractor;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -108,7 +111,8 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mController,
mKeyguardDismissUtil,
mKeyguardStateController,
mDialogLaunchAnimator
mDialogLaunchAnimator,
mPanelInteractor
);
mTile.initialize();
@@ -146,7 +150,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
assertNotNull(onStartRecordingClicked.getValue());
onStartRecordingClicked.getValue().run();
verify(mDialogLaunchAnimator).disableAllCurrentDialogsExitAnimations();
verify(mHost).collapsePanels();
verify(mPanelInteractor).collapsePanels();
}
// Test that the tile is active and labeled correctly when the controller is starting