快速访问钱包,快速访问钱包,快速访问钱包,快速访问钱包

Android 11 提供的快速访问钱包功能允许用户直接从电源菜单访问支付卡和相关通行证。主要用例包括在 NFC 终端执行交易之前选择合适的支付方式,以及快速访问航班和其他通行证以迎接即将到来的活动。

在 Android 12 或更高版本中,快速访问钱包功能可从阴影中使用,如图 1 和图 2 所示。

阴影中的快速访问钱包功能
图 1.快速访问钱包功能(设备锁定)。
阴影中的快速访问钱包功能
图 2.快速访问钱包功能(设备解锁)。

在 Android 11 中,该功能可从电源菜单中使用,如图 3 所示。

电源菜单中的快速访问钱包功能
图 3.电源菜单中的快速访问钱包功能。

要求

您的设备必须具有 NFC 才能使用快速访问钱包功能。该功能绑定到默认 NFC 支付应用程序的QuickAccessWalletService ,这意味着设备还必须支持 NFC基于主机的卡模拟 (HCE)

功能概述

快速访问钱包有两个部分:快速访问钱包 UI 和快速访问钱包卡提供商。

在 Android 12 或更高版本中,Wallet UI 在 System UI 中运行,位于frameworks/base/packages/SystemUI/src/com/android/systemui/wallet中。在 Android 11 中,必须安装位于platform/packages/apps/QuickAccessWallet中的 Wallet UI 并将其列入白名单。

快速访问钱包卡提供商是默认的 NFC 支付应用程序。用户可以同时安装多个 NFC 支付应用,但只有默认的 NFC 支付应用才能在电源菜单上显示卡片。您可以指定最初将哪个 NFC 支付应用程序设置为默认应用程序,但用户可以在“设置”中选择其他应用程序。如果只安装了一个 NFC 支付应用程序,它会自动成为默认应用程序(请参阅CardEmulationManager )。

执行

要向 Quick Access Wallet UI 提供卡片,NFC 支付应用程序必须实现QuickAccessWalletService 。支付应用程序必须包含广告服务的清单条目。

为确保只有 System UI 可以绑定QuickAccessWalletService ,NFC 支付应用必须需要android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE权限。要求此权限可确保只有系统 UI 可以绑定到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 交易并因此启动一个活动来显示付款的进度和结果,它还应该尝试获取对绑定的QuickAccessWalletService的引用并使用事件类型 TYPE_NFC_PAYMENT_STARTED 调用QuickAccessWalletService#sendEvent TYPE_NFC_PAYMENT_STARTED .这会导致快速访问钱包 UI 被关闭,从而使用户可以畅通无阻地查看支付活动。

有关实现QuickAccessWalletService的其他文档,请参阅QuickAccessWalletServiceTestQuickAccessWalletService CTS 测试。

在 Android 11 中启用快速访问钱包 UI

要将 Quick Access Wallet 配置为可从 Android 11 的电源菜单中使用,请在构建中包含QuickAccessWallet目标,并通过将以下代码示例中的粗体行添加到overlay/frameworks/base/packages/SystemUI/res/values/config.xml来启用globalactions.wallet插件overlay/frameworks/base/packages/SystemUI/res/values/config.xml文件。

<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 支付应用没有导出此服务,则钱包 UI 是隐藏的。

QuickAccessWalletService 实现细节

QuickAccessWalletService具有三个必须实现的抽象方法: onWalletCardsRequestedonWalletCardSelectedonWalletDismissed 。下面的序列图说明了在 NFC 支付之前查看快速访问钱包时的调用序列。

快速访问钱包时序图

查看快速访问钱包时的示例调用顺序
图 4.查看 Quick Access Wallet 时的示例调用序列。

并非快速访问钱包的所有视图都跟随 NFC 支付,但上面的图 4 说明了QuickAccessWalletService的所有功能。在此示例中,快速访问钱包卡提供商实现了蓝色轮廓的元素。假设支付卡存储在设备上的数据库中,并通过名为PaymentCardManager的接口进行访问。进一步假设名为PaymentActivity的活动显示 NFC 支付的结果。流程如下:

  1. 用户执行手势以调出快速访问钱包。
  2. Quick Access Wallet UI(系统 UI 的一部分)检查包管理器以查看默认 NFC 支付应用程序是否导出QuickAccessWalletService

    • 如果服务未导出,则不会显示快速访问钱包。
  3. Quick Access Wallet UI 绑定到QuickAccessWalletService并调用onWalletCardsRequested 。此方法接受一个请求对象,其中包含有关可提供的卡片数量和大小的数据以及一个回调。可以从后台线程调用回调。

  4. QuickAccessWalletService计算它想要显示的卡片,然后在提供的回调上调用onSuccess方法。建议服务在后台线程上执行这些操作。

  5. 卡片一显示,系统 UI 就会通过调用onWalletCardSelected通知QuickAccessWalletService第一张卡片已被选中。

    • 每次用户选择一张新卡时都会调用onWalletCardSelected
    • 即使当前选择的卡没有更改,也可能会调用onWalletCardSelected
  6. 当用户关闭快速访问钱包时,系统 UI 通过调用onWalletDismissed QuickAccessWalletService

在上面的示例中,用户在显示钱包时将手机带入 NFC 支付终端的范围内。处理 NFC 支付的一个关键组件是HostApduService ,必须实现它来处理 NFC 读取器提供的 APDU(有关更多信息,请参阅基于主机的卡模拟)。假设支付应用启动了一个activity来显示与NFC终端交互的进度和结果。但是,快速访问钱包 UI 显示在应用程序窗口的顶部,这意味着支付活动被快速访问钱包 UI 遮挡。为了纠正这个问题,应用程序必须通知系统 UI 应该关闭快速访问钱包 UI。它可以通过获取对绑定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的 manifest 条目必须需要 Android 11 中引入的android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE权限。这是系统 UI 拥有的签名级权限,这意味着只有 System UI 进程才能绑定到QuickAccessWalletService的实现。请注意,侧面加载的应用程序可以申请此权限,并在运行 Android 10 或更低版本的设备上获得对QuickAccessWalletService数据的完全访问权限。为防止出现这种情况,建议该服务检查onCreate中的构建版本,并仅在运行 Android 11 及更高版本的设备上启用该服务。除了提供主机卡模拟支付服务所需的权限外,不需要其他应用权限。

如果默认 NFC 支付应用未实现或导出QuickAccessWalletService ,则不会显示快速访问钱包 UI。

Android 12 中的设置

要从锁定屏幕启用或禁用快速访问钱包,用户可以使用设置>显示>锁定屏幕中的显示钱包切换。要在阴影中禁用钱包,用户必须在快速设置阴影中手动编辑它。

从锁定屏幕切换以启用或禁用钱包

图 5.在设置的锁定屏幕页面中显示钱包切换。

Android 11 中的设置

用户可以从“设置”应用关闭快速访问钱包功能。设置页面位于Settings > System > Gestures > Cards & pass

设置页面以启用或禁用快速访问钱包功能
图 6.启用或禁用快速访问钱包功能的设置页面。

定制

将快速访问钱包视图添加到系统 UI 中的另一个位置

Quick Access Wallet UI是作为系统插件构建的。尽管 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);
  }
}

快速访问钱包 UI实现了GlobalActionsPanelPluginPanelViewControllerGlobalActionsDialog使用com.android.systemui.Dependency获取钱包插件的实例:

GlobalActionsPanelPlugin mPanelPlugin =
    Dependency.get(ExtensionController.class)
        .newExtension(GlobalActionsPanelPlugin.class)
        .withPlugin(GlobalActionsPanelPlugin.class)
        .build()
        .get();

在检查插件非空且onPanelShown返回的PanelViewController非空后,对话框将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();

要从电源菜单中删除快速访问钱包,请从系统构建中省略QuickAccessWallet目标。要从电源菜单中删除快速访问钱包但将其添加到不同的系统 UI 提供的视图中,请包含构建目标并从GlobalActionsImpl中删除对GlobalActionsPanelPlugin的引用。

设置默认配置

安卓 12

在 Android 12 或更高版本中,快速访问钱包始终在快速设置阴影中可见。锁定屏幕中快速访问钱包的可见性由以下安全设置控制: LOCKSCREEN_SHOW_WALLET 。此设置控制快速访问钱包图标是否显示在锁定屏幕的右下角。此设置默认设置为true ,但用户可以在Settings > Display > Lock screen > Show wallet中关闭。

安卓 11

在 Android 11 中,快速访问电子钱包的可见性由两个安全设置控制: GLOBAL_ACTIONS_PANEL_ENABLEDGLOBAL_ACTIONS_PANEL_AVAILABLEAVAILABLE设置控制是否可以在“设置”中打开和关闭该功能。此设置由WalletPluginService设置为true 。如果QuickAccessWallet未包含在构建中,则设置保持为falseENABLED设置在同一位置默认设置为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已启用并导出但未提供任何卡片,则 Shade 中的磁贴将如图 7 中的示例所示。单击该磁贴将打开默认的 NFC 支付应用程序。

    阴影中显示默认 NFC 支付应用程序的示例图块

    图 7.阴影中显示默认 NFC 支付应用程序的示例图块。

  • 单击图 8 所示的空白状态视图会打开默认的 NFC 支付应用程序。此空状态视图仅在用户在钱包中剩下一张卡时显示,从卡详细信息页面中删除该卡,然后返回到钱包视图。

  • 锁定屏幕显示钱包图标。

快速访问钱包中的空状态视图

图 8. Quick Access Wallet UI 中的空状态视图。

非零状态

  • 如果钱包应用程序提供一张或多张卡片,阴影中的图块将如图 9 所示。

    当钱包应用程序有一张或多张卡片时,阴影中的示例图块

    图 9.当钱包应用程序有一张或多张卡片时,阴影中的示例图块。

  • 单击图块会显示卡片轮播。

  • 锁定屏幕显示一个用于打开快速访问钱包的按钮。

    显示卡片的快速访问钱包 UI

    图 10.显示卡片的快速访问钱包 UI。

  • 如果显示的卡代表 NFC 支付方式,则将手机靠近 NFC 支付终端会导致使用该支付方式并关闭钱包视图。

  • 单击显示的卡片会打开该卡片的详细活动。

  • 如果QuickAccessWalletService提供多张卡片,用户可以在卡片之间滑动。

  • 溢出菜单包含一个条目:打开锁定屏幕设置,以便用户可以更改显示钱包选项。

锁定状态测试

  • 如果手机被锁定,钱包在快速设置阴影中可见,如果默认支付应用程序中不存在卡,则显示添加卡,或者如果默认支付应用程序中存在卡,则解锁以使用
  • 如果手机被锁定,则锁定屏幕上的钱包可见性由Secure.LOCKSCREEN_SHOW_WALLET设置控制,该设置在设置中控制。
  • 如果手机处于锁定状态, LOCKSCREEN_SHOW_WALLETfalse ,且默认 NFC 支付应用中不存在卡,则钱包不会显示在锁定屏幕上。
  • 如果手机被锁定, LOCKSCREEN_SHOW_WALLETtrue ,并且默认 NFC 支付应用中不存在卡,则钱包不会显示在锁定屏幕上。
  • 如果手机被锁定, LOCKSCREEN_SHOW_WALLETtrue ,并且卡存在于默认的 NFC 支付应用程序中,则钱包显示在锁定屏幕上。
  • 在锁屏界面显示钱包时解锁手机会导致卡片被重新查询,这可能会导致卡片内容不同。

可访问性测试

  • 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已启用并导出但未提供任何卡片,则快速访问钱包 UI 将显示空状态视图。
  • 单击空白状态视图会打开钱包应用程序。

    快速访问钱包 UI 中的空状态视图
    图 11. Quick Access Wallet UI 中的空状态视图。

非零状态

  • 如果钱包应用程序提供一张或多张卡片,卡片会显示在快速访问钱包 UI 中。

    显示卡片的快速访问钱包 UI
    图 12.显示卡片的快速访问钱包 UI。
  • 如果显示的卡代表 NFC 支付方式,则将手机靠近 NFC 支付终端会导致使用该支付方式并关闭钱包视图。

  • 单击显示的卡片会关闭钱包视图并打开该卡片的详细活动。

  • 如果QuickAccessWalletService提供多张卡片,用户可以在卡片之间滑动。

  • 溢出菜单包含两个条目:一个打开钱包应用程序,另一个打开设置中的显示卡和通行证屏幕。

锁定状态测试

  • 如果手机被锁定,钱包可见性由Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT设置控制,可以在设置中控制。
  • 如果手机被锁定并且POWER_MENU_LOCK_SHOW_CONTENTfalse ,则不会显示钱包。
  • 如果手机被锁定并且POWER_MENU_LOCK_SHOW_CONTENTtrue ,则显示钱包。
  • 在锁屏显示钱包时解锁手机会导致重新查询卡片,这可能会导致卡片内容不同。

可访问性测试

  • TalkBack 用户可以通过左右滑动和收听卡片的内容描述来导航钱包视图。
  • 在启用 TalkBack 的情况下左右滑动会依次选择每张卡片。 TalkBack 用户可以在 NFC 支付终端上选择和使用 NFC 支付方式。