summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_wallet.xml11
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/flags.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java207
9 files changed, 382 insertions, 2 deletions
diff --git a/packages/SystemUI/res/drawable/ic_qs_wallet.xml b/packages/SystemUI/res/drawable/ic_qs_wallet.xml
new file mode 100644
index 000000000000..e146eabecfd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_wallet.xml
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,
+ -0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18v-6h16v6zM20,8L4,8L4,6h16v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b0c5239bb4b8..af6df32a02b0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 8cd5757247e2..2163806666d9 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -38,6 +38,8 @@
<!-- People Tile flag -->
<bool name="flag_conversations">false</bool>
+ <bool name="flag_wallet">false</bool>
+
<!-- The new animations to/from lockscreen and AOD! -->
<bool name="flag_lockscreen_animations">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 87fa4f831e37..78180a7a80a8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1621,6 +1621,12 @@
<!-- Name of the alarm status bar icon. -->
<string name="status_bar_alarm">Alarm</string>
+ <!-- Wallet strings -->
+ <!-- Wallet empty state, title [CHAR LIMIT=32] -->
+ <string name="wallet_title">Wallet</string>
+ <!-- Secondary label of the quick access wallet tile. [CHAR LIMIT=32] -->
+ <string name="wallet_secondary_label">Ready</string>
+
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 123ccee80179..8e344d2c2df7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -31,6 +31,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -360,4 +361,11 @@ public class DependencyProvider {
public ModeSwitchesController providesModeSwitchesController(Context context) {
return new ModeSwitchesController(context);
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ public QuickAccessWalletClient provideQuickAccessWalletClient(Context context) {
+ return QuickAccessWalletClient.create(context);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 29b9e64d1659..ba349c6273d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -47,6 +47,7 @@ import com.android.systemui.qs.tiles.LocationTile;
import com.android.systemui.qs.tiles.MicrophoneToggleTile;
import com.android.systemui.qs.tiles.NfcTile;
import com.android.systemui.qs.tiles.NightDisplayTile;
+import com.android.systemui.qs.tiles.QuickAccessWalletTile;
import com.android.systemui.qs.tiles.ReduceBrightColorsTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.ScreenRecordTile;
@@ -93,6 +94,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
private final Provider<AlarmTile> mAlarmTileProvider;
+ private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -129,7 +131,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<CameraToggleTile> cameraToggleTileProvider,
Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
Provider<DeviceControlsTile> deviceControlsTileProvider,
- Provider<AlarmTile> alarmTileProvider) {
+ Provider<AlarmTile> alarmTileProvider,
+ Provider<QuickAccessWalletTile> quickAccessWalletTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -161,6 +164,7 @@ public class QSFactoryImpl implements QSFactory {
mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
mDeviceControlsTileProvider = deviceControlsTileProvider;
mAlarmTileProvider = alarmTileProvider;
+ mQuickAccessWalletTileProvider = quickAccessWalletTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -224,6 +228,8 @@ public class QSFactoryImpl implements QSFactory {
return mDeviceControlsTileProvider.get();
case "alarm":
return mAlarmTileProvider.get();
+ case "wallet":
+ return mQuickAccessWalletTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
new file mode 100644
index 000000000000..60c5d1cafde9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quicksettings.Tile;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Quick access wallet **/
+public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
+
+ private static final String FEATURE_CHROME_OS = "org.chromium.arc";
+ private final CharSequence mLabel = mContext.getString(R.string.wallet_title);
+ // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes.
+ private final QuickAccessWalletClient mQuickAccessWalletClient;
+ private final KeyguardStateController mKeyguardStateController;
+ private final PackageManager mPackageManager;
+ private final SecureSettings mSecureSettings;
+ private final FeatureFlags mFeatureFlags;
+
+ @Inject
+ public QuickAccessWalletTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ QuickAccessWalletClient quickAccessWalletClient,
+ KeyguardStateController keyguardStateController,
+ PackageManager packageManager,
+ SecureSettings secureSettings,
+ FeatureFlags featureFlags) {
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mQuickAccessWalletClient = quickAccessWalletClient;
+ mKeyguardStateController = keyguardStateController;
+ mPackageManager = packageManager;
+ mSecureSettings = secureSettings;
+ mFeatureFlags = featureFlags;
+ }
+
+
+ @Override
+ public State newTileState() {
+ State state = new State();
+ state.handlesLongClick = false;
+ return state;
+ }
+
+ @Override
+ protected void handleClick() {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+ }
+
+ @Override
+ protected void handleUpdateState(State state, Object arg) {
+ CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
+ state.label = qawLabel == null ? mLabel : qawLabel;
+ state.contentDescription = state.label;
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_wallet);
+ boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
+ if (mQuickAccessWalletClient.isWalletFeatureAvailable()) {
+ state.state = isDeviceLocked ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE;
+ state.secondaryLabel = isDeviceLocked
+ ? null
+ : mContext.getString(R.string.wallet_secondary_label);
+ state.stateDescription = state.secondaryLabel;
+ } else {
+ state.state = Tile.STATE_UNAVAILABLE;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mFeatureFlags.isQuickAccessWalletEnabled()
+ && mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
+ && !mPackageManager.hasSystemFeature(FEATURE_CHROME_OS)
+ && mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
+ return qawLabel == null ? mLabel : qawLabel;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index c8e0d60ce304..c3de81c3c66a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -83,6 +83,10 @@ public class FeatureFlags {
return mFlagReader.isEnabled(R.bool.flag_monet);
}
+ public boolean isQuickAccessWalletEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_wallet);
+ }
+
public boolean isNavigationBarOverlayEnabled() {
return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
new file mode 100644
index 000000000000..33166ccadc27
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION;
+import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quicksettings.Tile;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class QuickAccessWalletTileTest extends SysuiTestCase {
+
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
+ @Mock
+ private QuickAccessWalletClient mQuickAccessWalletClient;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private SecureSettings mSecureSettings;
+ @Mock
+ private FeatureFlags mFeatureFlags;
+
+ private TestableLooper mTestableLooper;
+ private QuickAccessWalletTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
+ when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
+
+ mTile = new QuickAccessWalletTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mQuickAccessWalletClient,
+ mKeyguardStateController,
+ mPackageManager,
+ mSecureSettings,
+ mFeatureFlags);
+ }
+
+ @Test
+ public void testNewTile() {
+ assertFalse(mTile.newTileState().handlesLongClick);
+ }
+
+ @Test
+ public void testIsAvailable_featureFlagIsOff() {
+ when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(false);
+ assertFalse(mTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_qawServiceNotAvailable() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+ assertFalse(mTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_qawServiceAvailable() {
+ when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false);
+ when(mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT)).thenReturn("Component");
+
+ assertTrue(mTile.isAvailable());
+ }
+
+ @Test
+ public void testHandleClick_openGPay() {
+ Intent intent = new Intent("WalletIntent");
+ when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(intent);
+ mTile.handleClick();
+
+ verify(mActivityStarter, times(1))
+ .postStartActivityDismissingKeyguard(eq(intent), anyInt());
+ }
+
+ @Test
+ public void testHandleUpdateState_updateLabelAndIcon() {
+ QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
+ QSTile.State state = new QSTile.State();
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn("QuickAccessWallet");
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals("QuickAccessWallet", state.label.toString());
+ assertTrue(state.label.toString().contentEquals(state.contentDescription));
+ assertEquals(icon, state.icon);
+ }
+
+ @Test
+ public void testHandleUpdateState_deviceLocked_tileInactive() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_INACTIVE, state.state);
+ assertNull(state.stateDescription);
+ }
+
+ @Test
+ public void testHandleUpdateState_deviceLocked_tileActive() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_ACTIVE, state.state);
+ assertTrue(state.secondaryLabel.toString().contentEquals(state.stateDescription));
+ assertEquals(
+ getContext().getString(R.string.wallet_secondary_label),
+ state.secondaryLabel.toString());
+ }
+
+ @Test
+ public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_UNAVAILABLE, state.state);
+ }
+}