Merge "Keep status bar icons hidden during camera launch." into tm-qpr-dev am: 4f197f9a85

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

Change-Id: Ic0e7db8dbf1faa82d92d7fe4004871d5a08a2cc9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Josh Tsuji
2023-01-31 18:27:41 +00:00
committed by Automerger Merge Worker
4 changed files with 151 additions and 4 deletions

View File

@@ -23,6 +23,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewStub; import android.view.ViewStub;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LockIconView; import com.android.keyguard.LockIconView;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.battery.BatteryMeterView;
@@ -67,6 +68,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
@@ -299,7 +301,9 @@ public abstract class StatusBarViewModule {
OperatorNameViewController.Factory operatorNameViewControllerFactory, OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings, SecureSettings secureSettings,
@Main Executor mainExecutor, @Main Executor mainExecutor,
DumpManager dumpManager DumpManager dumpManager,
StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor
) { ) {
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory, return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
ongoingCallController, ongoingCallController,
@@ -320,7 +324,9 @@ public abstract class StatusBarViewModule {
operatorNameViewControllerFactory, operatorNameViewControllerFactory,
secureSettings, secureSettings,
mainExecutor, mainExecutor,
dumpManager); dumpManager,
statusBarWindowStateController,
keyguardUpdateMonitor);
} }
/** /**

View File

@@ -44,6 +44,7 @@ import android.widget.LinearLayout;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
@@ -72,6 +73,8 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener; import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener;
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
@@ -129,6 +132,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final SecureSettings mSecureSettings; private final SecureSettings mSecureSettings;
private final Executor mMainExecutor; private final Executor mMainExecutor;
private final DumpManager mDumpManager; private final DumpManager mDumpManager;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private List<String> mBlockedIcons = new ArrayList<>(); private List<String> mBlockedIcons = new ArrayList<>();
private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>();
@@ -164,6 +169,22 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
} }
}; };
/**
* Whether we've launched the secure camera over the lockscreen, but haven't yet received a
* status bar window state change afterward.
*
* We wait for this state change (which will tell us whether to show/hide the status bar icons)
* so that there is no flickering/jump cutting during the camera launch.
*/
private boolean mWaitingForWindowStateChangeAfterCameraLaunch = false;
/**
* Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
* a new status bar window state.
*/
private final StatusBarWindowStateListener mStatusBarWindowStateListener = state ->
mWaitingForWindowStateChangeAfterCameraLaunch = false;
@SuppressLint("ValidFragment") @SuppressLint("ValidFragment")
public CollapsedStatusBarFragment( public CollapsedStatusBarFragment(
StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
@@ -185,7 +206,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
OperatorNameViewController.Factory operatorNameViewControllerFactory, OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings, SecureSettings secureSettings,
@Main Executor mainExecutor, @Main Executor mainExecutor,
DumpManager dumpManager DumpManager dumpManager,
StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor
) { ) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController; mOngoingCallController = ongoingCallController;
@@ -207,6 +230,20 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mSecureSettings = secureSettings; mSecureSettings = secureSettings;
mMainExecutor = mainExecutor; mMainExecutor = mainExecutor;
mDumpManager = dumpManager; mDumpManager = dumpManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener);
}
@Override
public void onDestroy() {
super.onDestroy();
mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener);
} }
@Override @Override
@@ -254,6 +291,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
} }
@Override
public void onCameraLaunchGestureDetected(int source) {
mWaitingForWindowStateChangeAfterCameraLaunch = true;
}
@VisibleForTesting @VisibleForTesting
void updateBlockedIcons() { void updateBlockedIcons() {
mBlockedIcons.clear(); mBlockedIcons.clear();
@@ -466,6 +508,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
&& mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) { && mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) {
return true; return true;
} }
// When launching the camera over the lockscreen, the icons become visible momentarily
// before animating out, since we're not yet aware that the launching camera activity is
// fullscreen. Even once the activity finishes launching, it takes a short time before WM
// decides that the top app wants to hide the icons and tells us to hide them. To ensure
// that this high-visibility animation is smooth, keep the icons hidden during a camera
// launch until we receive a window state change which indicates that the activity is done
// launching and WM has decided to show/hide the icons. For extra safety (to ensure the
// icons don't remain hidden somehow) we double check that the camera is still showing, the
// status bar window isn't hidden, and we're still occluded as well, though these checks
// are typically unnecessary.
final boolean hideIconsForSecureCamera =
(mWaitingForWindowStateChangeAfterCameraLaunch ||
!mStatusBarWindowStateController.windowIsShowing()) &&
mKeyguardUpdateMonitor.isSecureCameraLaunchedOverKeyguard() &&
mKeyguardStateController.isOccluded();
if (hideIconsForSecureCamera) {
return true;
}
return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer(); return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
} }

View File

@@ -61,6 +61,10 @@ class StatusBarWindowStateController @Inject constructor(
listeners.add(listener) listeners.add(listener)
} }
fun removeListener(listener: StatusBarWindowStateListener) {
listeners.remove(listener)
}
/** Returns true if the window is currently showing. */ /** Returns true if the window is currently showing. */
fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING

View File

@@ -23,10 +23,12 @@ import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedul
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -45,6 +47,7 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
@@ -67,6 +70,8 @@ import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
@@ -79,6 +84,9 @@ import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true) @RunWithLooper(setAsMainLooper = true)
@SmallTest @SmallTest
@@ -118,6 +126,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock @Mock
private DumpManager mDumpManager; private DumpManager mDumpManager;
@Mock
private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>();
public CollapsedStatusBarFragmentTest() { public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class); super(CollapsedStatusBarFragment.class);
@@ -127,6 +141,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
public void setup() { public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mDependency.injectMockDependency(DarkIconDispatcher.class); mDependency.injectMockDependency(DarkIconDispatcher.class);
// Keep the window state listeners so we can dispatch to them to test the status bar
// fragment's response.
doAnswer(invocation -> {
mStatusBarWindowStateListeners.add(invocation.getArgument(0));
return null;
}).when(mStatusBarWindowStateController).addListener(
any(StatusBarWindowStateListener.class));
} }
@Test @Test
@@ -414,6 +436,27 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertFalse(contains); assertFalse(contains);
} }
@Test
public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mockSecureCameraLaunch(fragment, true /* launched */);
// Status icons should be invisible or gone, but certainly not VISIBLE.
assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertNotEquals(View.VISIBLE, getClockView().getVisibility());
mockSecureCameraLaunchFinished();
assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertNotEquals(View.VISIBLE, getClockView().getVisibility());
mockSecureCameraLaunch(fragment, false /* launched */);
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Override @Override
protected Fragment instantiate(Context context, String className, Bundle arguments) { protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -455,7 +498,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mOperatorNameViewControllerFactory, mOperatorNameViewControllerFactory,
mSecureSettings, mSecureSettings,
mExecutor, mExecutor,
mDumpManager); mDumpManager,
mStatusBarWindowStateController,
mKeyguardUpdateMonitor);
} }
private void setUpDaggerComponent() { private void setUpDaggerComponent() {
@@ -478,6 +523,35 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mNotificationAreaInner); mNotificationAreaInner);
} }
/**
* Configure mocks to return values consistent with the secure camera animating itself launched
* over the keyguard.
*/
private void mockSecureCameraLaunch(CollapsedStatusBarFragment fragment, boolean launched) {
when(mKeyguardUpdateMonitor.isSecureCameraLaunchedOverKeyguard()).thenReturn(launched);
when(mKeyguardStateController.isOccluded()).thenReturn(launched);
if (launched) {
fragment.onCameraLaunchGestureDetected(0 /* source */);
} else {
for (StatusBarWindowStateListener listener : mStatusBarWindowStateListeners) {
listener.onStatusBarWindowStateChanged(StatusBarManager.WINDOW_STATE_SHOWING);
}
}
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
}
/**
* Configure mocks to return values consistent with the secure camera showing over the keyguard
* with its launch animation finished.
*/
private void mockSecureCameraLaunchFinished() {
for (StatusBarWindowStateListener listener : mStatusBarWindowStateListeners) {
listener.onStatusBarWindowStateChanged(StatusBarManager.WINDOW_STATE_HIDDEN);
}
}
private CollapsedStatusBarFragment resumeAndGetFragment() { private CollapsedStatusBarFragment resumeAndGetFragment() {
mFragments.dispatchResume(); mFragments.dispatchResume();
processAllMessages(); processAllMessages();