diff options
| author | 2024-11-14 19:43:50 +0000 | |
|---|---|---|
| committer | 2024-12-03 15:41:20 +0000 | |
| commit | 392b900a19cdf84e0f9341f90b2b10d38c5d9740 (patch) | |
| tree | b251594160d6716d46129b5ee57cee32a8e30e81 | |
| parent | ed4e0aa7843f0daf28c957cb4af0aedb78b29a86 (diff) | |
Launch wallet app on double tap option.
Reads user settings on whether to launch camera or wallet on power
button double tap. If wallet has the setting, send PendingIntent retrieved from
QuickAccessWalletClient.getGestureTargetActivityPendingIntent.
Bug: 378469025
Test: atest GestureLauncherServiceTest
Test: Manual testing (double-tap power button with both settings and
ensure camera/wallet are appropriately launched.)
Flag: android.service.quickaccesswallet.launch_wallet_option_on_power_double_tap
Change-Id: Id14db9f33cd6f0de7527b06747d9520c3478a742
Change-Id: I808769cc5ee012a172427f98f585c025c516a386
4 files changed, 644 insertions, 121 deletions
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index 19bc8e31791e..d84a892e4f54 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -16,10 +16,14 @@ package com.android.server; +import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap; + import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow; import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis; import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -34,6 +38,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; +import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; @@ -42,10 +47,12 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; +import android.service.quickaccesswallet.QuickAccessWalletClient; import android.util.MutableBoolean; import android.util.Slog; import android.view.KeyEvent; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; @@ -71,7 +78,8 @@ public class GestureLauncherService extends SystemService { * Time in milliseconds in which the power button must be pressed twice so it will be considered * as a camera launch. */ - @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300; + @VisibleForTesting + static final long POWER_DOUBLE_TAP_MAX_TIME_MS = 300; /** @@ -101,10 +109,23 @@ public class GestureLauncherService extends SystemService { @VisibleForTesting static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000; - /** - * Number of taps required to launch camera shortcut. - */ - public static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2; + /** Indicates camera should be launched on power double tap. */ + @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0; + + /** Indicates wallet should be launched on power double tap. */ + @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1; + + /** Number of taps required to launch the double tap shortcut (either camera or wallet). */ + public static final int DOUBLE_POWER_TAP_COUNT_THRESHOLD = 2; + + /** Bundle to send with PendingIntent to grant background activity start privileges. */ + private static final Bundle GRANT_BACKGROUND_START_PRIVILEGES = + ActivityOptions.makeBasic() + .setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS) + .toBundle(); + + private final QuickAccessWalletClient mQuickAccessWalletClient; /** The listener that receives the gesture event. */ private final GestureEventListener mGestureListener = new GestureEventListener(); @@ -159,6 +180,9 @@ public class GestureLauncherService extends SystemService { */ private boolean mCameraDoubleTapPowerEnabled; + /** Whether wallet double tap power button gesture is currently enabled. */ + private boolean mWalletDoubleTapPowerEnabled; + /** * Whether emergency gesture is currently enabled */ @@ -205,16 +229,22 @@ public class GestureLauncherService extends SystemService { } } public GestureLauncherService(Context context) { - this(context, new MetricsLogger(), new UiEventLoggerImpl()); + this( + context, + new MetricsLogger(), + QuickAccessWalletClient.create(context), + new UiEventLoggerImpl()); } @VisibleForTesting public GestureLauncherService( Context context, MetricsLogger metricsLogger, + QuickAccessWalletClient quickAccessWalletClient, UiEventLogger uiEventLogger) { super(context); mContext = context; + mQuickAccessWalletClient = quickAccessWalletClient; mMetricsLogger = metricsLogger; mUiEventLogger = uiEventLogger; } @@ -240,6 +270,9 @@ public class GestureLauncherService extends SystemService { "GestureLauncherService"); updateCameraRegistered(); updateCameraDoubleTapPowerEnabled(); + if (launchWalletOptionOnPowerDoubleTap()) { + updateWalletDoubleTapPowerEnabled(); + } updateEmergencyGestureEnabled(); updateEmergencyGesturePowerButtonCooldownPeriodMs(); @@ -295,6 +328,14 @@ public class GestureLauncherService extends SystemService { } @VisibleForTesting + void updateWalletDoubleTapPowerEnabled() { + boolean enabled = isWalletDoubleTapPowerSettingEnabled(mContext, mUserId); + synchronized (this) { + mWalletDoubleTapPowerEnabled = enabled; + } + } + + @VisibleForTesting void updateEmergencyGestureEnabled() { boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId); synchronized (this) { @@ -421,10 +462,33 @@ public class GestureLauncherService extends SystemService { Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0); } + /** Checks if camera should be launched on double press of the power button. */ public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) { - return isCameraDoubleTapPowerEnabled(context.getResources()) - && (Settings.Secure.getIntForUser(context.getContentResolver(), - Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0); + boolean res; + + if (launchWalletOptionOnPowerDoubleTap()) { + res = isDoubleTapPowerGestureSettingEnabled(context, userId) + && getDoubleTapPowerGestureAction(context, userId) + == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER; + } else { + // These are legacy settings that will be deprecated once the option to launch both + // wallet and camera has been created. + res = isCameraDoubleTapPowerEnabled(context.getResources()) + && (Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0); + } + return res; + } + + /** Checks if wallet should be launched on double tap of the power button. */ + public static boolean isWalletDoubleTapPowerSettingEnabled(Context context, int userId) { + if (!launchWalletOptionOnPowerDoubleTap()) { + return false; + } + + return isDoubleTapPowerGestureSettingEnabled(context, userId) + && getDoubleTapPowerGestureAction(context, userId) + == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER; } public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) { @@ -444,6 +508,28 @@ public class GestureLauncherService extends SystemService { isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0; } + private static int getDoubleTapPowerGestureAction(Context context, int userId) { + return Settings.Secure.getIntForUser( + context.getContentResolver(), + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE, + LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER, + userId); + } + + /** Whether the shortcut to launch app on power double press is enabled. */ + private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) { + return Settings.Secure.getIntForUser( + context.getContentResolver(), + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, + isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0, + userId) + == 1; + } + + private static boolean isDoubleTapConfigEnabled(Resources resources) { + return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled); + } + /** * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The * value is capped at a maximum @@ -497,10 +583,16 @@ public class GestureLauncherService extends SystemService { * Whether GestureLauncherService should be enabled according to system properties. */ public static boolean isGestureLauncherEnabled(Resources resources) { - return isCameraLaunchEnabled(resources) - || isCameraDoubleTapPowerEnabled(resources) - || isCameraLiftTriggerEnabled(resources) - || isEmergencyGestureEnabled(resources); + boolean res = + isCameraLaunchEnabled(resources) + || isCameraLiftTriggerEnabled(resources) + || isEmergencyGestureEnabled(resources); + if (launchWalletOptionOnPowerDoubleTap()) { + res |= isDoubleTapConfigEnabled(resources); + } else { + res |= isCameraDoubleTapPowerEnabled(resources); + } + return res; } /** @@ -530,7 +622,7 @@ public class GestureLauncherService extends SystemService { mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps = 1; - } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) { + } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) { // Tap too slow for shortcuts mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; @@ -573,6 +665,7 @@ public class GestureLauncherService extends SystemService { return false; } boolean launchCamera = false; + boolean launchWallet = false; boolean launchEmergencyGesture = false; boolean intercept = false; long powerTapInterval; @@ -584,7 +677,7 @@ public class GestureLauncherService extends SystemService { mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps = 1; - } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) { + } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) { // Tap too slow for shortcuts mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; @@ -629,10 +722,16 @@ public class GestureLauncherService extends SystemService { } } if (mCameraDoubleTapPowerEnabled - && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) { + && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS + && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) { launchCamera = true; intercept = interactive; + } else if (launchWalletOptionOnPowerDoubleTap() + && mWalletDoubleTapPowerEnabled + && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS + && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) { + launchWallet = true; + intercept = interactive; } } if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) { @@ -651,6 +750,10 @@ public class GestureLauncherService extends SystemService { (int) powerTapInterval); mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); } + } else if (launchWallet) { + Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval=" + + powerTapInterval + "ms"); + launchWallet = sendGestureTargetActivityPendingIntent(); } else if (launchEmergencyGesture) { Slog.i(TAG, "Emergency gesture detected, launching."); launchEmergencyGesture = handleEmergencyGesture(); @@ -666,11 +769,74 @@ public class GestureLauncherService extends SystemService { mPowerButtonSlowConsecutiveTaps); mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval); - outLaunched.value = launchCamera || launchEmergencyGesture; + outLaunched.value = launchCamera || launchEmergencyGesture || launchWallet; // Intercept power key event if the press is part of a gesture (camera, eGesture) and the // user has completed setup. return intercept && isUserSetupComplete(); } + + /** + * Fetches and sends gestureTargetActivityPendingIntent from QuickAccessWallet, which is a + * specific activity that QuickAccessWalletService has defined to be launch on detection of the + * power button gesture. + */ + private boolean sendGestureTargetActivityPendingIntent() { + boolean userSetupComplete = isUserSetupComplete(); + if (mQuickAccessWalletClient == null + || !mQuickAccessWalletClient.isWalletServiceAvailable()) { + Slog.w(TAG, "QuickAccessWalletService is not available, ignoring wallet gesture."); + return false; + } + + if (!userSetupComplete) { + if (DBG) { + Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture."); + } + return false; + } + if (DBG) { + Slog.d(TAG, "userSetupComplete = true, performing wallet gesture."); + } + + mQuickAccessWalletClient.getGestureTargetActivityPendingIntent( + getContext().getMainExecutor(), + gesturePendingIntent -> { + if (gesturePendingIntent == null) { + Slog.d(TAG, "getGestureTargetActivityPendingIntent is null."); + sendFallbackPendingIntent(); + return; + } + sendPendingIntentWithBackgroundStartPrivileges(gesturePendingIntent); + }); + return true; + } + + /** + * If gestureTargetActivityPendingIntent is null, this method is invoked to start the activity + * that QuickAccessWalletService has defined to host the Wallet view, which is typically the + * home screen of the Wallet application. + */ + private void sendFallbackPendingIntent() { + mQuickAccessWalletClient.getWalletPendingIntent( + getContext().getMainExecutor(), + walletPendingIntent -> { + if (walletPendingIntent == null) { + Slog.w(TAG, "getWalletPendingIntent returns null. Not launching " + + "anything for wallet."); + return; + } + sendPendingIntentWithBackgroundStartPrivileges(walletPendingIntent); + }); + } + + private void sendPendingIntentWithBackgroundStartPrivileges(PendingIntent pendingIntent) { + try { + pendingIntent.send(GRANT_BACKGROUND_START_PRIVILEGES); + } catch (PendingIntent.CanceledException e) { + Slog.e(TAG, "PendingIntent was canceled", e); + } + } + /** * @return true if camera was launched, false otherwise. */ @@ -743,31 +909,39 @@ public class GestureLauncherService extends SystemService { Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; } - private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { - mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - mContext.getContentResolver().unregisterContentObserver(mSettingObserver); - registerContentObservers(); - updateCameraRegistered(); - updateCameraDoubleTapPowerEnabled(); - updateEmergencyGestureEnabled(); - updateEmergencyGesturePowerButtonCooldownPeriodMs(); - } - } - }; - - private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) { - public void onChange(boolean selfChange, android.net.Uri uri, int userId) { - if (userId == mUserId) { - updateCameraRegistered(); - updateCameraDoubleTapPowerEnabled(); - updateEmergencyGestureEnabled(); - updateEmergencyGesturePowerButtonCooldownPeriodMs(); - } - } - }; + private final BroadcastReceiver mUserReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { + mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + mContext.getContentResolver().unregisterContentObserver(mSettingObserver); + registerContentObservers(); + updateCameraRegistered(); + updateCameraDoubleTapPowerEnabled(); + if (launchWalletOptionOnPowerDoubleTap()) { + updateWalletDoubleTapPowerEnabled(); + } + updateEmergencyGestureEnabled(); + updateEmergencyGesturePowerButtonCooldownPeriodMs(); + } + } + }; + + private final ContentObserver mSettingObserver = + new ContentObserver(new Handler()) { + public void onChange(boolean selfChange, android.net.Uri uri, int userId) { + if (userId == mUserId) { + updateCameraRegistered(); + updateCameraDoubleTapPowerEnabled(); + if (launchWalletOptionOnPowerDoubleTap()) { + updateWalletDoubleTapPowerEnabled(); + } + updateEmergencyGestureEnabled(); + updateEmergencyGesturePowerButtonCooldownPeriodMs(); + } + } + }; private final class GestureEventListener implements SensorEventListener { @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e99f67d43145..e39906bdc366 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -91,7 +91,7 @@ import static com.android.hardware.input.Flags.modifierShortcutDump; import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; -import static com.android.server.GestureLauncherService.CAMERA_POWER_TAP_COUNT_THRESHOLD; +import static com.android.server.GestureLauncherService.DOUBLE_POWER_TAP_COUNT_THRESHOLD; import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser; import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; @@ -4150,7 +4150,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Intercept keys (don't send to app) for 3x, 4x, 5x gestures) - if (mPowerButtonConsecutiveTaps > CAMERA_POWER_TAP_COUNT_THRESHOLD) { + if (mPowerButtonConsecutiveTaps > DOUBLE_POWER_TAP_COUNT_THRESHOLD) { setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, event.getDownTime()); return true; } @@ -5952,7 +5952,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } return powerTapInterval < POWER_MULTI_PRESS_TIMEOUT_MILLIS - && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD; + && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD; } // The camera gesture will be detected by GestureLauncherService. diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index 2349def93b0c..9850cb09ae2b 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -16,7 +16,12 @@ package com.android.server; -import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; +import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP; +import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap; + +import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER; +import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER; +import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -24,19 +29,27 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.PendingIntent; import android.app.StatusBarManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.os.Looper; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; +import android.service.quickaccesswallet.QuickAccessWalletClient; import android.telecom.TelecomManager; import android.test.mock.MockContentResolver; import android.testing.TestableLooper; @@ -55,6 +68,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -62,6 +76,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Unit tests for {@link GestureLauncherService}. @@ -90,9 +106,32 @@ public class GestureLauncherServiceTest { private @Mock TelecomManager mTelecomManager; private @Mock MetricsLogger mMetricsLogger; @Mock private UiEventLogger mUiEventLogger; + @Mock private QuickAccessWalletClient mQuickAccessWalletClient; private MockContentResolver mContentResolver; private GestureLauncherService mGestureLauncherService; + private Context mInstrumentationContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + private static final String LAUNCH_TEST_WALLET_ACTION = "LAUNCH_TEST_WALLET_ACTION"; + private static final String LAUNCH_FALLBACK_ACTION = "LAUNCH_FALLBACK_ACTION"; + private PendingIntent mGesturePendingIntent = + PendingIntent.getBroadcast( + mInstrumentationContext, + 0, + new Intent(LAUNCH_TEST_WALLET_ACTION), + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT); + + private PendingIntent mFallbackPendingIntent = + PendingIntent.getBroadcast( + mInstrumentationContext, + 0, + new Intent(LAUNCH_FALLBACK_ACTION), + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @BeforeClass public static void oneTimeInitialization() { if (Looper.myLooper() == null) { @@ -115,9 +154,49 @@ public class GestureLauncherServiceTest { when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager); when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent()); + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); + + mGestureLauncherService = + new GestureLauncherService( + mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger); + + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + } - mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger, - mUiEventLogger); + private WalletLaunchedReceiver registerWalletLaunchedReceiver(String action) { + IntentFilter filter = new IntentFilter(action); + WalletLaunchedReceiver receiver = new WalletLaunchedReceiver(); + mInstrumentationContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); + return receiver; + } + + /** + * A simple {@link BroadcastReceiver} implementation that counts down a {@link CountDownLatch} + * when a matching message is received + */ + private static final class WalletLaunchedReceiver extends BroadcastReceiver { + private static final int TIMEOUT_SECONDS = 3; + + private final CountDownLatch mLatch; + + WalletLaunchedReceiver() { + mLatch = new CountDownLatch(1); + } + + @Override + public void onReceive(Context context, Intent intent) { + mLatch.countDown(); + context.unregisterReceiver(this); + } + + Boolean waitUntilShown() { + try { + return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return false; + } + } } @Test @@ -134,37 +213,123 @@ public class GestureLauncherServiceTest { @Test public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() { - withCameraDoubleTapPowerEnableConfigValue(false); - withCameraDoubleTapPowerDisableSettingValue(1); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerEnabledConfigValue(false); + withDoubleTapPowerGestureEnableSettingValue(false); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + } else { + withCameraDoubleTapPowerEnableConfigValue(false); + withCameraDoubleTapPowerDisableSettingValue(1); + } assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( mContext, FAKE_USER_ID)); } @Test public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() { - withCameraDoubleTapPowerEnableConfigValue(false); - withCameraDoubleTapPowerDisableSettingValue(0); - assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( - mContext, FAKE_USER_ID)); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerEnabledConfigValue(false); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } else { + withCameraDoubleTapPowerEnableConfigValue(false); + withCameraDoubleTapPowerDisableSettingValue(0); + assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } } @Test public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(1); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(false); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + } else { + withCameraDoubleTapPowerEnableConfigValue(true); + withCameraDoubleTapPowerDisableSettingValue(1); + } assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( mContext, FAKE_USER_ID)); } @Test public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + } else { + withCameraDoubleTapPowerEnableConfigValue(true); + withCameraDoubleTapPowerDisableSettingValue(0); + } assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( mContext, FAKE_USER_ID)); } @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER); + + assertFalse( + mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testIsWalletDoubleTapPowerSettingEnabled() { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER); + + assertTrue( + mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() { + withDoubleTapPowerEnabledConfigValue(false); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER); + + assertTrue( + mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(false); + withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER); + + assertFalse( + mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + + assertFalse( + mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled( + mContext, FAKE_USER_ID)); + } + + @Test public void testIsEmergencyGestureSettingEnabled_settingDisabled() { withEmergencyGestureEnabledConfigValue(true); withEmergencyGestureEnabledSettingValue(false); @@ -245,12 +410,9 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_firstPowerDownCameraPowerGestureOnInteractive() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); - long eventTime = INITIAL_EVENT_TIME_MILLIS + - CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + long eventTime = INITIAL_EVENT_TIME_MILLIS + POWER_DOUBLE_TAP_MAX_TIME_MS - 1; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); boolean interactive = true; @@ -284,8 +446,12 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() { - withCameraDoubleTapPowerEnableConfigValue(false); - withCameraDoubleTapPowerDisableSettingValue(1); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerGestureEnableSettingValue(false); + } else { + withCameraDoubleTapPowerEnableConfigValue(false); + withCameraDoubleTapPowerDisableSettingValue(1); + } mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -298,7 +464,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -329,8 +495,12 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() { - withCameraDoubleTapPowerEnableConfigValue(false); - withCameraDoubleTapPowerDisableSettingValue(1); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerGestureEnableSettingValue(false); + } else { + withCameraDoubleTapPowerEnableConfigValue(false); + withCameraDoubleTapPowerDisableSettingValue(1); + } mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -343,7 +513,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -422,10 +592,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupComplete() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); - withUserSetupCompleteValue(true); + enableCameraGesture(); long eventTime = INITIAL_EVENT_TIME_MILLIS; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, @@ -437,7 +604,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -470,15 +637,145 @@ public class GestureLauncherServiceTest { } @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void + testInterceptPowerKeyDown_fiveInboundPresses_walletAndEmergencyEnabled_bothLaunch() { + WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent); + enableEmergencyGesture(); + enableWalletGesture(); + + // First event + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true); + + assertTrue(receiver.waitUntilShown()); + + // Presses 3 and 4 should not trigger any gesture + for (int i = 0; i < 2; i++) { + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false); + } + + // Fifth button press should trigger the emergency flow + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true); + + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); + verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testInterceptPowerKeyDown_intervalInBoundsWalletPowerGesture() { + WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent); + enableWalletGesture(); + enableEmergencyGesture(); + + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true); + assertTrue(receiver.waitUntilShown()); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testInterceptPowerKeyDown_walletGestureOn_quickAccessWalletServiceUnavailable() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent); + enableWalletGesture(); + + // First event + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false); + + assertFalse(receiver.waitUntilShown()); + } + + @Test + public void testInterceptPowerKeyDown_walletGestureOn_userSetupIncomplete() { + WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent); + enableWalletGesture(); + withUserSetupCompleteValue(false); + + // First event + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + assertFalse(receiver.waitUntilShown()); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testInterceptPowerKeyDown_walletPowerGesture_nullPendingIntent() { + WalletLaunchedReceiver gestureReceiver = + registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(null); + WalletLaunchedReceiver fallbackReceiver = + registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION); + setUpWalletFallbackPendingIntent(mFallbackPendingIntent); + enableWalletGesture(); + enableEmergencyGesture(); + + // First event + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true); + + assertFalse(gestureReceiver.waitUntilShown()); + assertTrue(fallbackReceiver.waitUntilShown()); + } + + @Test + @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP) + public void testInterceptPowerKeyDown_walletPowerGesture_intervalOutOfBounds() { + WalletLaunchedReceiver gestureReceiver = + registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION); + setUpGetGestureTargetActivityPendingIntent(null); + WalletLaunchedReceiver fallbackReceiver = + registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION); + setUpWalletFallbackPendingIntent(mFallbackPendingIntent); + enableWalletGesture(); + enableEmergencyGesture(); + + // First event + long eventTime = INITIAL_EVENT_TIME_MILLIS; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS; + eventTime += interval; + sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); + + assertFalse(gestureReceiver.waitUntilShown()); + assertFalse(fallbackReceiver.waitUntilShown()); + } + + @Test public void testInterceptPowerKeyDown_fiveInboundPresses_cameraAndEmergencyEnabled_bothLaunch() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - withEmergencyGestureEnabledConfigValue(true); - withEmergencyGestureEnabledSettingValue(true); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); - mGestureLauncherService.updateEmergencyGestureEnabled(); - withUserSetupCompleteValue(true); + enableCameraGesture(); + enableEmergencyGesture(); // First button press does nothing long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -491,7 +788,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 2nd button triggers camera eventTime += interval; @@ -580,7 +877,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture (camera gesture disabled) for (int i = 0; i < 3; i++) { eventTime += interval; @@ -634,7 +931,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture, but intercepts action. for (int i = 0; i < 3; i++) { eventTime += interval; @@ -737,7 +1034,7 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } @@ -765,7 +1062,7 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } @@ -916,7 +1213,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE, @@ -947,9 +1244,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); withUserSetupCompleteValue(false); long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -962,7 +1257,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -995,9 +1290,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnInteractive() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); long eventTime = INITIAL_EVENT_TIME_MILLIS; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, @@ -1009,7 +1302,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1042,9 +1335,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnInteractive() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); long eventTime = INITIAL_EVENT_TIME_MILLIS; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, @@ -1087,8 +1378,12 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() { - withCameraDoubleTapPowerEnableConfigValue(false); - withCameraDoubleTapPowerDisableSettingValue(1); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerGestureEnableSettingValue(false); + } else { + withCameraDoubleTapPowerEnableConfigValue(false); + withCameraDoubleTapPowerDisableSettingValue(1); + } mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -1101,7 +1396,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1146,7 +1441,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1223,10 +1518,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupComplete() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); - withUserSetupCompleteValue(true); + enableCameraGesture(); long eventTime = INITIAL_EVENT_TIME_MILLIS; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, @@ -1238,7 +1530,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1272,9 +1564,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupIncomplete() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); withUserSetupCompleteValue(false); long eventTime = INITIAL_EVENT_TIME_MILLIS; @@ -1287,7 +1577,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1332,7 +1622,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1365,9 +1655,7 @@ public class GestureLauncherServiceTest { @Test public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnNotInteractive() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); - mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + enableCameraGesture(); long eventTime = INITIAL_EVENT_TIME_MILLIS; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, @@ -1423,7 +1711,7 @@ public class GestureLauncherServiceTest { sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false); //Second event; call processPowerKeyDown without calling interceptPowerKeyDown - final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; KeyEvent keyEvent = new KeyEvent( @@ -1455,7 +1743,7 @@ public class GestureLauncherServiceTest { * @return last event time. */ private long triggerEmergencyGesture() { - return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1); + return triggerEmergencyGesture(POWER_DOUBLE_TAP_MAX_TIME_MS - 1); } /** @@ -1514,6 +1802,27 @@ public class GestureLauncherServiceTest { .thenReturn(enableConfigValue); } + private void withDoubleTapPowerEnabledConfigValue(boolean enable) { + when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled)) + .thenReturn(enable); + } + + private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) { + Settings.Secure.putIntForUser( + mContentResolver, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, + enable ? 1 : 0, + UserHandle.USER_CURRENT); + } + + private void withDefaultDoubleTapPowerGestureAction(int action) { + Settings.Secure.putIntForUser( + mContentResolver, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE, + action, + UserHandle.USER_CURRENT); + } + private void withEmergencyGestureEnabledConfigValue(boolean enableConfigValue) { when(mResources.getBoolean( com.android.internal.R.bool.config_emergencyGestureEnabled)) @@ -1552,6 +1861,40 @@ public class GestureLauncherServiceTest { UserHandle.USER_CURRENT); } + private void setUpGetGestureTargetActivityPendingIntent(PendingIntent pendingIntent) { + doAnswer( + invocation -> { + QuickAccessWalletClient.GesturePendingIntentCallback callback = + (QuickAccessWalletClient.GesturePendingIntentCallback) + invocation.getArguments()[1]; + callback.onGesturePendingIntentRetrieved(pendingIntent); + return null; + }) + .when(mQuickAccessWalletClient) + .getGestureTargetActivityPendingIntent(any(), any()); + } + + private void setUpWalletFallbackPendingIntent(PendingIntent pendingIntent) { + doAnswer( + invocation -> { + QuickAccessWalletClient.WalletPendingIntentCallback callback = + (QuickAccessWalletClient.WalletPendingIntentCallback) + invocation.getArguments()[1]; + callback.onWalletPendingIntentRetrieved(pendingIntent); + return null; + }) + .when(mQuickAccessWalletClient) + .getWalletPendingIntent(any(), any()); + } + + private void enableWalletGesture() { + withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER); + withDoubleTapPowerGestureEnableSettingValue(true); + withDoubleTapPowerEnabledConfigValue(true); + + mGestureLauncherService.updateWalletDoubleTapPowerEnabled(); + withUserSetupCompleteValue(true); + } private void enableEmergencyGesture() { withEmergencyGestureEnabledConfigValue(true); @@ -1561,8 +1904,14 @@ public class GestureLauncherServiceTest { } private void enableCameraGesture() { - withCameraDoubleTapPowerEnableConfigValue(true); - withCameraDoubleTapPowerDisableSettingValue(0); + if (launchWalletOptionOnPowerDoubleTap()) { + withDoubleTapPowerEnabledConfigValue(true); + withDoubleTapPowerGestureEnableSettingValue(true); + withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER); + } else { + withCameraDoubleTapPowerEnableConfigValue(true); + withCameraDoubleTapPowerDisableSettingValue(0); + } mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); withUserSetupCompleteValue(true); } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 18ecfa19772d..f06b45e94f77 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -278,7 +278,7 @@ class TestPhoneWindowManager { mHandler = new Handler(mTestLooper.getLooper()); mContext = mockingDetails(context).isSpy() ? context : spy(context); mGestureLauncherService = spy(new GestureLauncherService(mContext, mMetricsLogger, - mUiEventLogger)); + mQuickAccessWalletClient, mUiEventLogger)); setUp(supportSettingsUpdate); mTestLooper.dispatchAll(); } |