Merge "Keep status bar icons hidden during camera launch." into tm-qpr-dev
This commit is contained in:
@@ -23,6 +23,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.LockIconView;
|
||||
import com.android.systemui.R;
|
||||
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.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.CarrierConfigTracker;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
@@ -299,7 +301,9 @@ public abstract class StatusBarViewModule {
|
||||
OperatorNameViewController.Factory operatorNameViewControllerFactory,
|
||||
SecureSettings secureSettings,
|
||||
@Main Executor mainExecutor,
|
||||
DumpManager dumpManager
|
||||
DumpManager dumpManager,
|
||||
StatusBarWindowStateController statusBarWindowStateController,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor
|
||||
) {
|
||||
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
|
||||
ongoingCallController,
|
||||
@@ -320,7 +324,9 @@ public abstract class StatusBarViewModule {
|
||||
operatorNameViewControllerFactory,
|
||||
secureSettings,
|
||||
mainExecutor,
|
||||
dumpManager);
|
||||
dumpManager,
|
||||
statusBarWindowStateController,
|
||||
keyguardUpdateMonitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.R;
|
||||
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.OngoingCallListener;
|
||||
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.CarrierConfigChangedListener;
|
||||
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
|
||||
@@ -129,6 +132,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
private final SecureSettings mSecureSettings;
|
||||
private final Executor mMainExecutor;
|
||||
private final DumpManager mDumpManager;
|
||||
private final StatusBarWindowStateController mStatusBarWindowStateController;
|
||||
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
|
||||
private List<String> mBlockedIcons = new ArrayList<>();
|
||||
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")
|
||||
public CollapsedStatusBarFragment(
|
||||
StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
|
||||
@@ -185,7 +206,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
OperatorNameViewController.Factory operatorNameViewControllerFactory,
|
||||
SecureSettings secureSettings,
|
||||
@Main Executor mainExecutor,
|
||||
DumpManager dumpManager
|
||||
DumpManager dumpManager,
|
||||
StatusBarWindowStateController statusBarWindowStateController,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor
|
||||
) {
|
||||
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
|
||||
mOngoingCallController = ongoingCallController;
|
||||
@@ -207,6 +230,20 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
mSecureSettings = secureSettings;
|
||||
mMainExecutor = mainExecutor;
|
||||
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
|
||||
@@ -254,6 +291,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraLaunchGestureDetected(int source) {
|
||||
mWaitingForWindowStateChangeAfterCameraLaunch = true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateBlockedIcons() {
|
||||
mBlockedIcons.clear();
|
||||
@@ -466,6 +508,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
&& mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ class StatusBarWindowStateController @Inject constructor(
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeListener(listener: StatusBarWindowStateListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
/** Returns true if the window is currently showing. */
|
||||
fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING
|
||||
|
||||
|
||||
@@ -23,10 +23,12 @@ import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedul
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -45,6 +47,7 @@ import android.widget.FrameLayout;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiBaseFragmentTest;
|
||||
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.ongoingcall.OngoingCallController;
|
||||
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.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
@@ -79,6 +84,9 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
@@ -118,6 +126,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
|
||||
@Mock
|
||||
private DumpManager mDumpManager;
|
||||
@Mock
|
||||
private StatusBarWindowStateController mStatusBarWindowStateController;
|
||||
@Mock
|
||||
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
|
||||
private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>();
|
||||
|
||||
public CollapsedStatusBarFragmentTest() {
|
||||
super(CollapsedStatusBarFragment.class);
|
||||
@@ -127,6 +141,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
public void setup() {
|
||||
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
|
||||
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
|
||||
@@ -414,6 +436,27 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
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
|
||||
protected Fragment instantiate(Context context, String className, Bundle arguments) {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -455,7 +498,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
mOperatorNameViewControllerFactory,
|
||||
mSecureSettings,
|
||||
mExecutor,
|
||||
mDumpManager);
|
||||
mDumpManager,
|
||||
mStatusBarWindowStateController,
|
||||
mKeyguardUpdateMonitor);
|
||||
}
|
||||
|
||||
private void setUpDaggerComponent() {
|
||||
@@ -478,6 +523,35 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
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() {
|
||||
mFragments.dispatchResume();
|
||||
processAllMessages();
|
||||
|
||||
Reference in New Issue
Block a user