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.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user