“快速访问电子钱包”功能从 Android 11 开始提供,可让用户直接通过电源菜单使用支付卡和相关的卡券。主要使用情形包括:在 NFC 终端执行交易之前选择适当的付款方式,以及针对即将进行的活动快速访问机票及其他卡券。
在 Android 12 或更高版本中,可以通过通知栏使用“快速访问电子钱包”功能,如图 1 和图 2 所示。
在 Android 11 中,可以通过电源菜单使用该功能,如图 3 所示。
要求
您的设备必须支持 NFC 才能使用“快速访问电子钱包”功能。此功能会绑定到默认的 NFC 支付应用的 QuickAccessWalletService
,这意味着设备还必须支持 NFC 基于主机的卡模拟 (HCE)。
功能概览
“快速访问电子钱包”功能包含两个部分:“快速访问电子钱包”界面和“快速访问电子钱包”卡提供程序。
在 Android 12 或更高版本中,电子钱包界面在系统界面中运行,它位于 frameworks/base/packages/SystemUI/src/com/android/systemui/wallet
中。在 Android 11 中,必须安装电子钱包界面(位于 platform/packages/apps/QuickAccessWallet
中)并将其列入白名单。
“快速访问电子钱包”卡提供程序是默认的 NFC 支付应用。用户可以同时安装多款 NFC 支付应用,但只有默认 NFC 支付应用对应的支付卡会显示在电源菜单中。您一开始可以指定将哪款 NFC 付款应用设置为默认应用,但用户可以在“设置”中选择其他应用。如果仅安装了一款 NFC 支付应用,该应用会自动成为默认应用(请参阅 CardEmulationManager
)。
实现
如要向“快速访问电子钱包”界面提供支付卡,NFC 支付应用必须实现 QuickAccessWalletService
。付款应用中必须包含一个清单条目,用于对该服务加以说明。
为了确保仅系统界面能够绑定到 QuickAccessWalletService
,NFC 支付应用必须要求 android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE
权限。要求此权限可确保仅系统界面能够绑定到 QuickAccessWalletService
。
<service
android:name=".MyQuickAccessWalletService"
android:label="@string/my_default_tile_label"
android:icon="@drawable/my_default_icon_label"
android:logo="@drawable/my_wallet_logo"
android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
<intent-filter>
<action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.quickaccesswallet"
android:resource="@xml/quickaccesswallet_configuration" />
<meta-data
android:name="android.quickaccesswallet.tile"
android:resource="@drawable/my_default_tile_icon"/>
</service>
与电子钱包有关的其他信息包含在关联的 XML 文件中:
<quickaccesswallet-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity"
android:shortcutLongLabel="@string/my_wallet_empty_state_text"
android:shortcutShortLabel="@string/my_wallet_button_text"
android:targetActivity="com.example.android.WalletActivity"/>
接下来,付款应用必须实现 QuickAccessWalletService
:
public class MyQuickAccessWalletService extends QuickAccessWalletService {
@Override
public void onWalletCardsRequested(
GetWalletCardsRequest request,
GetWalletCardsCallback callback) {
GetWalletCardsResponse response = // generate response
callback.onSuccess(response);
}
@Override
public void onWalletCardSelected(SelectWalletCardRequest request) {
// selecting a card should ensure that it is used when making an NFC payment
}
@Override
public void onWalletDismissed() {
// May un-select card if the wallet app has the concept of a 'default'
// payment method
}
}
如果 HostApduService
开始处理 NFC 交易并使系统因此启动一个 Activity 来显示付款的进度和结果,它还应尝试获取对已绑定的 QuickAccessWalletService
的引用,并调用事件类型为 TYPE_NFC_PAYMENT_STARTED
的 QuickAccessWalletService#sendEvent
。这会导致“快速访问电子钱包”界面关闭,从而向用户呈现无遮挡的付款活动视图。
如需查看有关如何实现 QuickAccessWalletService
的其他文档,请参阅 QuickAccessWalletService
和 TestQuickAccessWalletService
CTS 测试。
在 Android 11 中启用“快速访问电子钱包”界面
如需在 Android 11 中配置“快速访问电子钱包”,以使其能够通过电源菜单访问,请在 build 中添加 QuickAccessWallet
目标,并将以下代码示例中以粗体显示的行添加到 overlay/frameworks/base/packages/SystemUI/res/values/config.xml
文件中,以启用 globalactions.wallet
插件。
<resources> ... <!-- SystemUI Plugins that can be loaded on user builds. --> <string-array name="config_pluginWhitelist" translatable="false"> <item>com.android.systemui</item> <item>com.android.systemui.plugin.globalactions.wallet</item> </string-array> </resources>
使用 def_nfc_payment_component
在设置配置文件中指定默认的 NFC 支付应用。
默认的 NFC 支付应用必须公开 QuickAccessWalletService
才能向“快速访问电子钱包”提供支付卡。如果默认的 NFC 支付应用未导出此服务,那么电子钱包界面会处于隐藏状态。
QuickAccessWalletService 实现详情
QuickAccessWalletService
具有三个必须实现的抽象方法:onWalletCardsRequested
、onWalletCardSelected
和 onWalletDismissed
。下面的序列图说明了查看“快速访问电子钱包”时(之后即进行 NFC 支付)的调用序列。
并不是用户每次查看“快速访问电子钱包”之后都会进行 NFC 支付,但上面的图 4 说明了 QuickAccessWalletService
的所有功能。在本示例中,“快速访问电子钱包”卡提供程序实现了蓝色边框的元素。本示例假设支付卡存储在设备上的数据库中,并可通过名为 PaymentCardManager
的接口使用。示例中进一步假设名为 PaymentActivity
的 activity 会显示 NFC 支付的结果。具体流程如下所示:
- 用户执行手势以使系统显示“快速访问电子钱包”。
“快速访问电子钱包”界面(系统界面的一部分)检查软件包管理器,以查看默认的 NFC 支付应用是否导出了
QuickAccessWalletService
。- 如果该服务未导出,系统不会显示“快速访问电子钱包”。
“快速访问电子钱包”界面绑定到
QuickAccessWalletService
,并调用onWalletCardsRequested
。此方法会采用一个请求对象(该对象包含可提供的支付卡数量和大小的相关数据)和一个回调。此回调可从后台线程调用。QuickAccessWalletService
计算它要显示的支付卡,然后对提供的回调调用onSuccess
方法。我们建议服务在后台线程上执行这些操作。支付卡显示后,系统界面通过调用
onWalletCardSelected
通知QuickAccessWalletService
已选择首张支付卡。- 每当用户选择新的支付卡时,系统都会调用
onWalletCardSelected
。 - 即使当前选择的支付卡未更改,系统也可能会调用
onWalletCardSelected
。
- 每当用户选择新的支付卡时,系统都会调用
当用户关闭“快速访问电子钱包”时,系统界面会通过调用
onWalletDismissed
来通知QuickAccessWalletService
。
在上面的示例中,用户在系统显示钱包时将手机放入 NFC 支付终端的感应范围内。处理 NFC 付款的主要组件是 HostApduService
,必须实现该组件才能处理 NFC 读取器提供的 APDU(如需了解详情,请参阅基于主机的卡模拟)。本示例假设付款应用会启动一个 Activity 来显示与 NFC 终端互动的进度和结果。但是,“快速访问电子钱包”界面会叠加在应用窗口上显示,也就是说,付款活动视图会被“快速访问电子钱包”界面遮挡。如需解决此问题,应用必须通知系统界面应关闭“快速访问电子钱包”界面。这可以通过以下方法完成:获取对已绑定的 QuickAccessWalletService
的引用,并调用事件类型为 TYPE_NFC_PAYMENT_STARTED
的 sendWalletServiceEvent
。
QuickAccessWalletService 实现示例
/** Sample implementation of {@link QuickAccessWalletService} */
@RequiresApi(VERSION_CODES.R)
public class MyQuickAccessWalletService extends QuickAccessWalletService {
private static final String TAG = "QAWalletSvc";
private ExecutorService executor;
private PaymentCardManager paymentCardManager;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Log.w(TAG, "Should not run on pre-R devices");
stopSelf();
return;
}
executor = Executors.newSingleThreadExecutor();
paymentCardManager = new PaymentCardManager();
}
@Override
public void onDestroy() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}
executor.shutdownNow();
}
@Override
public void onWalletCardsRequested(
@NonNull GetWalletCardsRequest request, @NonNull GetWalletCardsCallback callback) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}
executor.submit(
() -> {
List<PaymentCard> paymentCards = paymentCardManager.getCards();
int maxCards = Math.min(paymentCards.size(), request.getMaxCards());
List<WalletCard> walletCards = new ArrayList<>(maxCards);
int selectedIndex = 0;
int cardWidthPx = request.getCardWidthPx();
int cardHeightPx = request.getCardHeightPx();
for (int index = 0; index < maxCards; index++) {
PaymentCard paymentCard = paymentCards.get(index);
WalletCard walletCard =
new WalletCard.Builder(
paymentCard.getCardId(),
paymentCard.getCardImage(cardWidthPx, cardHeightPx),
paymentCard.getContentDescription(),
paymentCard.getPendingIntent())
.build();
walletCards.add(walletCard);
if (paymentCard.isSelected()) {
selectedIndex = index;
}
}
GetWalletCardsResponse response =
new GetWalletCardsResponse(walletCards, selectedIndex);
callback.onSuccess(response);
});
}
@Override
public void onWalletCardSelected(@NonNull SelectWalletCardRequest request) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}
executor.submit(
() -> paymentCardManager.selectCardById(request.getCardId()));
}
@Override
public void onWalletDismissed() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}
executor.submit(() -> {
paymentCardManager.removeCardOverrides();
});
}
}
如需详细了解 QuickAccessWalletService
,请参阅 QuickAccessWalletService
API 参考文档。
权限
QuickAccessWalletService
的清单条目必须要求获得 Android 11 中引入的 android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE
权限。这是系统界面拥有的签名级权限,这意味着只有系统界面进程才能绑定到 QuickAccessWalletService
的实现。请注意,旁加载应用可以声明此权限,并获得对搭载 Android 10 或更低版本的设备上的 QuickAccessWalletService
数据的完整访问权限。为防止出现这种情况,我们建议该服务在 onCreate
中检查 build 版本号,并仅在搭载 Android 11 及更高版本的设备上启用该服务。除了提供主机卡模拟付款服务所需的权限外,无需提供任何其他应用权限。
如果默认的 NFC 支付应用未实现或导出 QuickAccessWalletService
,系统不会显示“快速访问电子钱包”界面。
Android 12 中的设置
如需在锁定屏幕上启用或停用“快速访问电子钱包”,用户可以使用设置 > 显示 > 锁定屏幕中的显示电子钱包切换开关。如需在通知栏中停用电子钱包,用户必须在快捷设置栏中手动进行修改。
图 5. “设置”中“锁定屏幕”页面上的“显示电子钱包”切换开关。
Android 11 中的设置
用户可以从“设置”应用中关闭“快速访问电子钱包”功能。依次点击设置 > 系统 > 手势 > 银行卡和卡券可找到设置页面。
自定义
将“快速访问电子钱包”视图添加到系统界面中的其他位置
“快速访问电子钱包”界面是以系统插件的形式构建的。尽管 AOSP 实现在 GlobalActionsDialog
中使用该界面(长按电源按钮后显示),但您也可以将该功能改为通过其他手势使用,前提是要维护插件接口指定的协定。
public interface GlobalActionsPanelPlugin extends Plugin {
/** Invoked when the view is shown */
PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked);
/** Callbacks for interacting with the view container */
interface Callbacks {
/** Dismisses the view */
void dismissGlobalActionsMenu();
/** Starts a PendingIntent, dismissing the keyguard if necessary. */
void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent);
}
/** Provides the Quick Access Wallet view */
interface PanelViewController {
/** Returns the QuickAccessWallet view, which may take any size */
View getPanelContent();
/** Invoked when the view is dismissed */
void onDismissed();
/** Invoked when the device is either locked or unlocked. */
void onDeviceLockStateChanged(boolean locked);
}
}
“快速访问电子钱包”界面实现 GlobalActionsPanelPlugin
和 PanelViewController
。GlobalActionsDialog
通过使用 com.android.systemui.Dependency
获取电子钱包插件的实例。
GlobalActionsPanelPlugin mPanelPlugin =
Dependency.get(ExtensionController.class)
.newExtension(GlobalActionsPanelPlugin.class)
.withPlugin(GlobalActionsPanelPlugin.class)
.build()
.get();
在确认插件非 null 以及 onPanelShown
返回的 PanelViewController
非 null 后,该对话框会将 getPanelContent
提供的 View
附加到自己的 View
中,并针对系统事件提供适当的回调。
// Construct a Wallet PanelViewController.
// `this` implements GlobalActionsPanelPlugin.Callbacks
GlobalActionsPanelPlugin.PanelViewController mPanelController =
mPanelPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());
// Attach the view
FrameLayout panelContainer = findViewById(R.id.my_panel_container);
FrameLayout.LayoutParams panelParams =
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
panelContainer.addView(mPanelController.getPanelContent(), panelParams);
// Respond to unlock events (if the view can be accessed while the phone is locked)
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
boolean unlocked = keyguardStateController.isUnlocked()
|| keyguardStateController.canDismissLockScreen();
mPanelController.onDeviceLockStateChanged(unlocked);
}
});
// Implement GlobalActionsPanelPlugin.Callbacks
@Override
public void dismissGlobalActionsMenu() {
dismissDialog();
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
}
// Notify the wallet when the container view is dismissed
mPanelController.onDismissed();
如需从电源菜单中移除“快速访问电子钱包”,请在系统 build 中省略 QuickAccessWallet
目标。若要从电源菜单中移除“快速访问电子钱包”,但将其添加到其他系统界面提供的视图中,请添加 build 目标,并从 GlobalActionsImpl
中移除对 GlobalActionsPanelPlugin
的引用。
设置默认配置
Android 12
在 Android 12 或更高版本中,“快速访问电子钱包”始终会显示在快捷设置栏上。锁定屏幕中是否显示“快速访问电子钱包”受以下安全设置控制:LOCKSCREEN_SHOW_WALLET
。此设置用于控制锁定屏幕的右下角是否显示“快速访问电子钱包”图标。这项设置默认设置为 true
,但用户可以前往设置 > 显示 > 锁定屏幕 > 显示电子钱包将其关闭。
Android 11
在 Android 11 中,是否显示“快速访问电子钱包”受以下两项安全设置控制:GLOBAL_ACTIONS_PANEL_ENABLED
和 GLOBAL_ACTIONS_PANEL_AVAILABLE
。AVAILABLE
设置用于控制能否在“设置”中启用和停用此功能。此设置由 WalletPluginService
设为 true
。如果 build 中不包含 QuickAccessWallet
,此设置仍为 false
。默认情况下,ENABLED
设置在同一位置设为 true
,但用户可以在“设置”中停用它。如需更改默认行为,请修改 WalletPluginService#enableFeatureInSettings
。
验证
如需验证“快速访问电子钱包”的实现情况,请运行 CTS 测试和手动测试。如果对该插件进行了更改,那么还应运行包含的 robolectric 测试。
CTS 测试
运行位于 cts/tests/quickaccesswallet
的 CTS 测试。
针对 Android 12 的手动测试
如需测试“快速访问电子钱包”的核心功能,您需要具有一个 NFC 支付终端(真实或虚拟都可以)和一个实现了 QuickAccessWalletService
的 NFC 支付应用(电子钱包应用)。必须测试的核心功能包括可用性、零状态、支付卡选择和锁定屏幕行为。
可用性
- 如果默认的 NFC 支付应用不支持该功能,您将无法在快捷设置和锁定屏幕中使用“快速访问电子钱包”。
- 如果默认的 NFC 支付应用支持该功能,您可以从快捷设置栏访问“快速访问电子钱包”。
- 如果默认的 NFC 支付应用支持该功能,并且
LOCKSCREEN_SHOW_WALLET
设置为true
,您可以在锁定屏幕上访问“快速访问电子钱包”。 - 如果默认的 NFC 支付应用支持该功能,并且
LOCKSCREEN_SHOW_WALLET
设置为false
,您无法在锁定屏幕上访问“快速访问电子钱包”。
零状态
如果
QuickAccessWalletService
已启用并导出,但未提供任何支付卡,那么通知栏中的图块会如图 7 中的示例所示。点击该图块即可打开默认的 NFC 支付应用。图 7. 通知栏中显示默认 NFC 支付应用的示例图块。
点击图 8 中所示的空状态视图会打开默认的 NFC 支付应用。仅当用户在电子钱包中留有一张支付卡,从卡详情页面中移除该卡,然后返回电子钱包视图时,系统才会显示这个空状态视图。
锁定屏幕会显示电子钱包图标。
图 8. “快速访问电子钱包”界面中的空状态视图。
非零状态
如果电子钱包应用提供一张或多张支付卡,通知栏中的图块会如图 9 所示。
图 9. 电子钱包应用中有一张或多张支付卡时通知栏中的图块示例。
点击图块会显示支付卡轮播界面。
锁定屏幕上会显示一个按钮,点击该按钮可打开“快速访问电子钱包”。
图 10. 显示一张支付卡的“快速访问电子钱包”界面。
如果显示的支付卡代表一种 NFC 支付方式,那么将手机靠近 NFC 支付终端会使系统使用该支付方式,并关闭电子钱包视图。
点击所显示的支付卡会打开该支付卡的详细活动。
如果
QuickAccessWalletService
提供了多张支付卡,用户可在这些支付卡之间滑动切换。溢出菜单包含一个条目:打开锁定屏幕设置,以便用户更改显示电子钱包选项。
锁定状态测试
- 如果手机处于锁定状态,那么电子钱包会显示在快捷设置栏中,并在默认付款应用中没有支付卡时显示添加卡的相关说明,或者在默认付款应用中有支付卡时显示解锁设备即可使用的相关说明。
- 如果手机处于锁定状态,锁定屏幕上是否显示电子钱包由
Secure.LOCKSCREEN_SHOW_WALLET
设置控制,而后者可在“设置”中进行控制。 - 如果手机处于锁定状态,
LOCKSCREEN_SHOW_WALLET
为false
,并且默认的 NFC 支付应用中没有支付卡,那么锁定屏幕上不会显示电子钱包。 - 如果手机处于锁定状态,
LOCKSCREEN_SHOW_WALLET
为true
,并且默认的 NFC 支付应用中没有支付卡,那么锁定屏幕上不会显示电子钱包。 - 如果手机处于锁定状态,
LOCKSCREEN_SHOW_WALLET
为true
,并且默认的 NFC 支付应用中有支付卡,那么锁定屏幕上会显示电子钱包。 - 在锁定屏幕上显示电子钱包时解锁手机会使系统重新查询支付卡,而这可能会导致显示不同的支付卡内容。
无障碍功能测试
- TalkBack 用户可以通过左右滑动并听取支付卡内容说明来浏览电子钱包视图。
- 在启用 TalkBack 的情况下,左右滑动会依次选择每张支付卡。TalkBack 用户可以在 NFC 支付终端选择并使用某种 NFC 支付方式。
针对 Android 11 的手动测试
如需测试“快速访问电子钱包”的核心功能,您需要具有一个 NFC 支付终端(真实或虚拟都可以)和一个实现了 QuickAccessWalletService
的 NFC 支付应用(电子钱包应用)。必须测试的核心功能包括可用性、零状态、支付卡选择和锁定屏幕行为。
可用性
- 如果
GLOBAL_ACTIONS_PANEL_ENABLED
设置设为true
且默认的 NFC 支付应用支持此功能,就可以使用“快速访问电子钱包”功能。 - 如果
GLOBAL_ACTIONS_PANEL_ENABLED
设置设为false
且默认的 NFC 支付应用支持此功能,就无法使用“快速访问电子钱包”功能。 - 如果
GLOBAL_ACTIONS_PANEL_ENABLED
设置设为true
且默认的 NFC 支付应用不支持此功能,就无法使用“快速访问电子钱包”功能。 - 如果
GLOBAL_ACTIONS_PANEL_ENABLED
设置设为false
且默认的 NFC 支付应用不支持此功能,就无法使用“快速访问电子钱包”功能。
零状态
- 如果
QuickAccessWalletService
已启用并导出,但未提供任何支付卡,那么“快速访问电子钱包”界面会显示空状态视图。 点击空状态视图会打开电子钱包应用。
非零状态
如果电子钱包应用提供一张或多张支付卡,这些支付卡会显示在“快速访问电子钱包”界面中。
如果显示的支付卡代表一种 NFC 支付方式,那么将手机靠近 NFC 支付终端会使系统使用该支付方式,并关闭电子钱包视图。
点击屏幕上显示的支付卡会关闭电子钱包视图,并打开该支付卡的详细活动。
如果
QuickAccessWalletService
提供了多张支付卡,用户可在这些支付卡之间滑动切换。溢出菜单包含两个条目:一个用于打开电子钱包应用,另一个用于打开“设置”中的显示银行卡和卡券屏幕。
锁定状态测试
- 如果手机处于锁定状态,是否显示电子钱包由
Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT
设置控制,而后者可在“设置”中进行控制。 - 如果手机处于锁定状态且
POWER_MENU_LOCK_SHOW_CONTENT
为false
,系统不会显示电子钱包。 - 如果手机处于锁定状态且
POWER_MENU_LOCK_SHOW_CONTENT
为true
,系统就会显示电子钱包。 - 在锁定屏幕上显示电子钱包时解锁手机会使系统重新查询支付卡,而这可能会导致显示不同的支付卡内容。
无障碍功能测试
- TalkBack 用户可以通过左右滑动并听取支付卡内容说明来浏览电子钱包视图。
- 在启用 TalkBack 的情况下,左右滑动会依次选择每张支付卡。TalkBack 用户可以在 NFC 支付终端选择并使用某种 NFC 支付方式。