Merge "Add Unit Tests to guarantee non Pip feature device quality"

This commit is contained in:
TreeHugger Robot
2020-09-21 10:26:40 +00:00
committed by Android (Google) Code Review
9 changed files with 378 additions and 15 deletions

View File

@@ -62,6 +62,7 @@ import com.android.internal.os.SomeArgs;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.phone.PipMenuActivityController;
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.pip.phone.PipUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -260,6 +261,11 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
if (!PipUtils.hasSystemFeature(context)) {
Log.w(TAG, "Device not support PIP feature");
return;
}
mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
displayController.addDisplayWindowListener(this);
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
@@ -30,7 +29,6 @@ import android.app.IActivityManager;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -278,9 +276,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mContext = context;
mActivityManager = ActivityManager.getService();
PackageManager pm = context.getPackageManager();
boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
if (supportsPip) {
if (PipUtils.hasSystemFeature(mContext)) {
initController(context, broadcastDispatcher, configController, deviceConfig,
displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
pipSurfaceTransactionHelper, pipTaskOrganizer, pipUiEventLogger);

View File

@@ -18,6 +18,7 @@ package com.android.systemui.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -57,4 +58,14 @@ public class PipUtils {
}
return new Pair<>(null, 0);
}
/**
* The util to check if device has PIP feature
*
* @param context application context
* @return true if device has PIP feature, false otherwise.
*/
public static boolean hasSystemFeature(Context context) {
return context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
}
}

View File

@@ -16,7 +16,6 @@
package com.android.systemui.recents;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
@@ -65,6 +64,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
@@ -77,6 +77,7 @@ import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.phone.PipUtils;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -155,7 +156,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
private boolean mSupportsRoundedCornersOnWindows;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@VisibleForTesting
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@Override
public void startScreenPinning(int taskId) {
@@ -624,7 +626,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
mHasPipFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
mHasPipFeature = PipUtils.hasSystemFeature(mContext);
mStatusBarOptionalLazy = statusBarOptionalLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;

View File

@@ -40,6 +40,7 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.phone.PipUtils;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.tracing.ProtoTraceable;
@@ -57,7 +58,6 @@ import com.android.wm.shell.protolog.ShellProtoLogImpl;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Optional;
@@ -103,7 +103,6 @@ public final class WMShell extends SystemUI
ProtoTracer protoTracer) {
super(context);
mCommandQueue = commandQueue;
mCommandQueue.addCallback(this);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mActivityManagerWrapper = activityManagerWrapper;
mDisplayImeController = displayImeController;
@@ -120,6 +119,7 @@ public final class WMShell extends SystemUI
@Override
public void start() {
mCommandQueue.addCallback(this);
// This is to prevent circular init problem by separating registration step out of its
// constructor. And make sure the initialization of DisplayImeController won't depend on
// specific feature anymore.
@@ -131,6 +131,9 @@ public final class WMShell extends SystemUI
@VisibleForTesting
void initPip(Pip pip) {
if (!PipUtils.hasSystemFeature(mContext)) {
return;
}
mCommandQueue.addCallback(new CommandQueue.Callbacks() {
@Override
public void showPictureInPictureMenu() {

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2020 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.pip.phone;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Unit tests for {@link PipController}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PipControllerTest extends SysuiTestCase {
private PipController mPipController;
private TestableContext mSpiedContext;
@Mock private ActivityManagerWrapper mMockActivityManagerWrapper;
@Mock private BroadcastDispatcher mMockBroadcastDispatcher;
@Mock private ConfigurationController mMockConfigurationController;
@Mock private DeviceConfigProxy mMockDeviceConfigProxy;
@Mock private DisplayController mMockdDisplayController;
@Mock private FloatingContentCoordinator mMockFloatingContentCoordinator;
@Mock private PackageManager mPackageManager;
@Mock private PipBoundsHandler mMockPipBoundsHandler;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@Mock private PipUiEventLogger mPipUiEventLogger;
@Mock private SysUiState mMockSysUiState;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
mPipController = new PipController(mSpiedContext, mMockBroadcastDispatcher,
mMockConfigurationController, mMockDeviceConfigProxy, mMockdDisplayController,
mMockFloatingContentCoordinator, mMockSysUiState, mMockPipBoundsHandler,
mMockPipSurfaceTransactionHelper, mMockPipTaskOrganizer, mPipUiEventLogger);
}
@Test
public void testNonPipDevice_shouldNotRegisterTaskStackListener() {
verify(mMockActivityManagerWrapper, never()).registerTaskStackListener(any());
}
@Test
public void testNonPipDevice_shouldNotRegisterPipTransitionCallback() {
verify(mMockPipTaskOrganizer, never()).registerPipTransitionCallback(any());
}
@Test
public void testNonPipDevice_shouldNotAddDisplayChangingController() {
verify(mMockdDisplayController, never()).addDisplayChangingController(any());
}
@Test
public void testNonPipDevice_shouldNotAddDisplayWindowListener() {
verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
}
@Test
public void testNonPipDevice_shouldNotAddCallback() {
verify(mMockConfigurationController, never()).addCallback(any());
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2020 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.pip.phone;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
/**
* Unit tests for {@link PipTaskOrganizer}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PipTaskOrganizerTest extends SysuiTestCase {
private PipTaskOrganizer mSpiedPipTaskOrganizer;
private TestableContext mSpiedContext;
@Mock private DisplayController mMockdDisplayController;
@Mock private PackageManager mPackageManager;
@Mock private PipBoundsHandler mMockPipBoundsHandler;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mMockPipBoundsHandler,
mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
mMockPipUiEventLogger, mMockShellTaskOrganizer));
}
@Test
public void testNonPipDevice_shellTaskOrganizer_shouldNotAddListener() {
verify(mMockShellTaskOrganizer, never()).addListener(any(), anyInt());
}
@Test
public void testNonPipDevice_displayController_shouldNotAddDisplayWindowListener() {
verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2020 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.recents;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
import dagger.Lazy;
/**
* Unit tests for {@link com.android.systemui.recents.OverviewProxyService}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OverviewProxyServiceTest extends SysuiTestCase {
private OverviewProxyService mSpiedOverviewProxyService;
private TestableContext mSpiedContext;
@Mock private BroadcastDispatcher mMockBroadcastDispatcher;
@Mock private CommandQueue mMockCommandQueue;
@Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy;
@Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener;
@Mock private NavigationModeController mMockNavModeController;
@Mock private NotificationShadeWindowController mMockStatusBarWinController;
@Mock private Optional<Pip> mMockPipOptional;
@Mock private Optional<SplitScreen> mMockSplitScreenOptional;
@Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
@Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
@Mock private PackageManager mPackageManager;
@Mock private SysUiState mMockSysUiState;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
mMockSysUiState, mMockPipOptional, mMockSplitScreenOptional,
mMockStatusBarOptionalLazy, mMockOneHandedOptional,
mMockBroadcastDispatcher));
}
@Test
public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished();
verify(mMockPipOptional, never()).ifPresent(any());
}
@Test
public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener(
mMockPinnedStackAnimationListener);
verify(mMockPipOptional, never()).ifPresent(any());
}
@Test
public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */,
100 /* shelfHeight */);
verify(mMockPipOptional, never()).ifPresent(any());
}
}

View File

@@ -16,12 +16,17 @@
package com.android.systemui.wmshell;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContext;
import androidx.test.runner.AndroidJUnit4;
@@ -68,6 +73,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock OneHanded mOneHanded;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock ProtoTracer mProtoTracer;
@Mock PackageManager mMockPackageManager;
@Before
public void setUp() {
@@ -89,8 +95,23 @@ public class WMShellTest extends SysuiTestCase {
public void initPip_registersCommandQueueCallback() {
mWMShell.initPip(mPip);
// Once for the shell, once for pip
verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
}
@Test
public void nonPipDevice_shouldNotInitPip() {
TestableContext spiedContext = spy(mContext);
when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager);
final WMShell nonPipWMShell = new WMShell(spiedContext, mCommandQueue,
mKeyguardUpdateMonitor,
mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
nonPipWMShell.initPip(mPip);
verify(mCommandQueue, never()).addCallback(any());
}
@Test
@@ -108,8 +129,7 @@ public class WMShellTest extends SysuiTestCase {
mWMShell.initOneHanded(mOneHanded);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
// Once for the shell, once for the one handed mode
verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mNavigationModeController).addListener(
any(NavigationModeController.ModeChangedListener.class));