快速訪問錢包,快速訪問錢包,快速訪問錢包,快速訪問錢包

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 支付方式。
,

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。

  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking on a displayed card opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains one entry: open the lock screen settings so that the user can change the Show wallet option.

Lock state tests

  • If the phone is locked, the wallet is visible on the quick settings shade, with a description of Add a card if no card exists in the default payment app, or unlock to use if cards exist in the default payment app.
  • If the phone is locked, wallet visibility on the lock screen is controlled by the Secure.LOCKSCREEN_SHOW_WALLET setting, which is controlled in Settings.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is false , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and cards exist in the default NFC payment app, the wallet is displayed on the lock screen.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being requeried, which might result in different card content.

Accessibility tests

  • Talkback users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with Talkback enabled selects each card in turn. Talkback users can select and use an NFC payment method at an NFC payment terminal.

Manual tests for Android 11

Testing the core features of the Quick Access Wallet requires an NFC payment terminal (real or fake) and an NFC payment app that implements QuickAccessWalletService (wallet app). Core features that must be tested include availability, zero state, card selection, and lock screen behavior.

Availability

  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app supports the feature, the Quick Access Wallet is accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app supports the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.

Zero state

  • If QuickAccessWalletService is enabled and exported but doesn't provide any cards, the Quick Access Wallet UI displays the empty state view.
  • Clicking the empty state view opens the wallet app.

    Empty state view in the Quick Access Wallet UI
    Figure 11. Empty state view in the Quick Access Wallet UI.

Non-zero state

  • If the wallet app provides one or more cards, the cards are displayed in the Quick Access Wallet UI.

    Quick Access Wallet UI with a card displayed
    Figure 12. Quick Access Wallet UI with a card displayed.
  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking a displayed card dismisses the wallet view and opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains two entries: one that opens the wallet app and one that opens the Show cards & passes screen in Settings.

Lock state tests

  • If the phone is locked, wallet visibility is controlled by the Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT setting, which can be controlled in Settings.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is false , the wallet isn't displayed.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is true , the wallet is displayed.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being re-queried, which might result in different card content.

Accessibility tests

  • TalkBack users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with TalkBack enabled selects each card in turn. TalkBack users can select and use an NFC payment method at an NFC payment terminal.
,

The Quick Access Wallet feature, available from Android 11, allows the user to access payment cards and relevant passes directly from the power menu. Major use cases include selecting the appropriate payment method before performing a transaction at an NFC terminal and quickly accessing flights and other passes for upcoming events.

In Android 12 or higher, the Quick Access Wallet feature is available from the shade as shown in Figure 1 and Figure 2.

Quick Access Wallet feature in shade
Figure 1. Quick Access Wallet feature (device locked).
Quick Access Wallet feature in shade
Figure 2. Quick Access Wallet feature (device unlocked).

In Android 11, the feature is available from the power menu as shown in Figure 3.

Quick Access Wallet feature in power menu
Figure 3. Quick Access Wallet feature in power menu.

Requirements

Your device must have NFC to use the Quick Access Wallet feature. The feature binds to QuickAccessWalletService of the default NFC payment app, which means that the device must also support NFC host-based card emulation (HCE) .

Feature overview

There are two parts to the Quick Access Wallet: the Quick Access Wallet UI and the Quick Access Wallet card provider.

In Android 12 or higher, the Wallet UI runs in System UI and is located in frameworks/base/packages/SystemUI/src/com/android/systemui/wallet . In Android 11, the Wallet UI, which is located in platform/packages/apps/QuickAccessWallet , must be installed and whitelisted.

The Quick Access Wallet card provider is the default NFC payment app. Users can have multiple NFC payment apps installed simultaneously, but only the default NFC payment app can show cards on the power menu. You can specify which NFC payment app is set as the default initially, but users can select a different app in Settings. If only one NFC payment app is installed, it becomes the default automatically (see CardEmulationManager ).

執行

To provide cards to the Quick Access Wallet UI, NFC payment apps must implement QuickAccessWalletService . Payment apps must include a manifest entry advertising the service.

To ensure that only the System UI can bind to QuickAccessWalletService , the NFC payment app must require the android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission. Requiring this permission ensures that only the System UI can bind to 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>

Additional information about the wallet is included in the linked XML file:

<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"/>

Next, the payment app must implement 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
    }
}

If HostApduService starts to handle an NFC transaction and, as a consequence, starts an activity to display the progress and outcome of the payment, it should also try to get a reference to the bound QuickAccessWalletService and call QuickAccessWalletService#sendEvent with an event type of TYPE_NFC_PAYMENT_STARTED . This causes the Quick Access Wallet UI to be dismissed, thus allowing the user an unobstructed view of the payment activity.

For additional documentation on implementing QuickAccessWalletService , see QuickAccessWalletService and the TestQuickAccessWalletService CTS test.

Enabling Quick Access Wallet UI in Android 11

To configure the Quick Access Wallet to be available from the power menu in Android 11, include the QuickAccessWallet target in the build and enable the globalactions.wallet plugin by adding the line in bold in the code sample below to the overlay/frameworks/base/packages/SystemUI/res/values/config.xml file.

<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>

Specify the default NFC payment app in the settings configuration file using def_nfc_payment_component .

The default NFC payment app must expose QuickAccessWalletService to provide cards to the Quick Access Wallet. If the default NFC payment app doesn't export this service, the wallet UI is hidden.

QuickAccessWalletService implementation details

QuickAccessWalletService has three abstract methods that must be implemented: onWalletCardsRequested , onWalletCardSelected , and onWalletDismissed . The sequence diagram below illustrates a call sequence when the Quick Access Wallet is viewed immediately preceding an NFC payment.

Quick Access Wallet sequence diagram

Example call sequence when Quick Access Wallet is viewed
Figure 4. Example call sequence when Quick Access Wallet is viewed.

Not all views of the Quick Access Wallet are followed by an NFC payment, but Figure 4 above illustrates all of the capabilities of QuickAccessWalletService . In this example, the Quick Access Wallet card provider implements the elements outlined in blue. It's assumed that payment cards are stored on the device in a database and are accessed through an interface named PaymentCardManager . It's further assumed that an activity called PaymentActivity displays the result of an NFC payment. The flow proceeds as follows:

  1. The user performs a gesture to bring up the Quick Access Wallet.
  2. The Quick Access Wallet UI (part of System UI) checks the package manager to see if the default NFC payment app exports QuickAccessWalletService .

    • If the service isn't exported, the Quick Access Wallet isn't displayed.
  3. The Quick Access Wallet UI binds to the QuickAccessWalletService and calls onWalletCardsRequested . This method takes a request object containing data about the number and size of the cards that can be provided and a callback. The callback can be called from a background thread.

  4. QuickAccessWalletService computes the cards that it wants to show, then calls the onSuccess method on the provided callback. It's recommended that the service perform these actions on a background thread.

  5. As soon as the cards are displayed, the System UI notifies QuickAccessWalletService that the first card has been selected by calling onWalletCardSelected .

    • onWalletCardSelected is called every time the user selects a new card.
    • onWalletCardSelected might be called even if the currently selected card hasn't changed.
  6. When the user dismisses the Quick Access Wallet, the System UI notifies QuickAccessWalletService by calling onWalletDismissed .

In the above example, the user brings the phone into range of an NFC payment terminal while the wallet is being displayed. A key component of handling NFC payments is HostApduService , which must be implemented to process APDUs provided by the NFC reader (for more information, see Host-based card emulation ). It's assumed that the payment app starts an activity to display the progress and result of the interaction with the NFC terminal. However, the Quick Access Wallet UI is displayed on top of the app window, meaning that the payment activity is obscured by the Quick Access Wallet UI. To rectify this, the app must notify the System UI that the Quick Access Wallet UI should be dismissed. It can do so by getting a reference to the bound QuickAccessWalletService and calling sendWalletServiceEvent with the event type TYPE_NFC_PAYMENT_STARTED .

QuickAccessWalletService sample implementation

/** 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();
    });
  }
}

For further details about QuickAccessWalletService , see QuickAccessWalletService API reference .

Permissions

The manifest entry for QuickAccessWalletService must require the android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission introduced in Android 11. This is a signature-level permission held by the System UI, which means that only the System UI process can bind to implementations of QuickAccessWalletService . Be aware that side-loaded apps can claim this permission and get full access to QuickAccessWalletService data on devices running Android 10 or lower. To prevent this, it's recommended that the service check the build version in onCreate and enable the service only on devices running Android 11 and higher. No other app permissions are required beyond those necessary to provide host card emulation payment services.

If the default NFC payment app doesn't implement or export QuickAccessWalletService , the Quick Access Wallet UI isn't displayed.

Settings in Android 12

To enable or disable the Quick Access Wallet from the lock screen, users can use the Show wallet toggle in Settings > Display > Lock screen . To disable the wallet in the shade, users must manually edit it in the quick settings shade.

Toggle to enable or disable wallet from lock screen

Figure 5. Show wallet toggle in the Lock screen page in Settings.

Settings in Android 11

Users can turn off the Quick Access Wallet feature from the Settings app. The settings page is found in Settings > System > Gestures > Cards & passes .

Settings page to enable or disable the Quick Access Wallet feature
Figure 6. Settings page to enable or disable the Quick Access Wallet feature.

定制

Adding Quick Access Wallet view to another location in System UI

The Quick Access Wallet UI is built as a system plugin . Although the AOSP implementation makes use of it in GlobalActionsDialog (shown on long power press), you can move the feature behind a different gesture as long as you maintain the contract specified by the plugin interface.

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);
  }
}

The Quick Access Wallet UI implements GlobalActionsPanelPlugin and PanelViewController . GlobalActionsDialog gets an instance of the wallet plugin by using com.android.systemui.Dependency :

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

After checking that the plugin is non-null and that the PanelViewController returned by onPanelShown is non-null, the dialog attaches the View provided by getPanelContent to its own View and provides appropriate callbacks for system events.

// 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();

To remove the Quick Access Wallet from the power menu, omit the QuickAccessWallet target from the system build. To remove the Quick Access Wallet from the power menu but add it to a different System UI provided view, include the build target and remove references to the GlobalActionsPanelPlugin from GlobalActionsImpl .

Setting default configurations

Android 12

In Android 12 or higher, the Quick Access Wallet is always visible in the quick settings shade. Visibility of the Quick Access Wallet in the lock screen is gated by the following secure setting: LOCKSCREEN_SHOW_WALLET . This setting controls whether the Quick Access Wallet icon is shown on the bottom right of the lock screen. This setting is set to true by default, but can be turned off by the user in Settings > Display > Lock screen > Show wallet .

Android 11

In Android 11, Quick Access Wallet visibility is gated by two secure settings: GLOBAL_ACTIONS_PANEL_ENABLED and GLOBAL_ACTIONS_PANEL_AVAILABLE . The AVAILABLE setting controls whether the feature can be turned on and off in Settings. This setting is set to true by WalletPluginService . If QuickAccessWallet isn't included in the build, then the setting remains false . The ENABLED setting is set to true by default in the same place, but can be turned off by the user in Settings. To change the default behavior, modify WalletPluginService#enableFeatureInSettings .

驗證

To validate your implementation of the Quick Access Wallet, run CTS and manual tests. Changes to the plugin should also exercise the included robolectric tests .

CTS tests

Run the CTS tests located at cts/tests/quickaccesswallet .

Manual tests for Android 12

Testing the core features of the Quick Access Wallet requires an NFC payment terminal (real or fake) and an NFC payment app that implements QuickAccessWalletService (wallet app). Core features that must be tested include: availability, zero state, card selection, and lock screen behavior.

Availability

  • If the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible in neither the quick settings nor the lock screen.
  • If the default NFC payment app supports the feature, the Quick Access Wallet is accessible in the quick settings shade.
  • If the default NFC payment app supports the feature and if the LOCKSCREEN_SHOW_WALLET setting is true , the Quick Access Wallet is accessible on the lock screen.
  • If the default NFC payment app supports the feature and if the LOCKSCREEN_SHOW_WALLET setting is false , the Quick Access Wallet isn't accessible on the lock screen.

Zero state

  • If QuickAccessWalletService is enabled and exported but doesn't provide any cards, the tile in the Shade appears as shown in the example in Figure 7. Clicking on the tile opens the default NFC payment app.

    Example tile in the shade showing default NFC payment app

    Figure 7. Example tile in the shade showing default NFC payment app.

  • Clicking on the empty state view as shown in Figure 8 opens the default NFC payment app. This empty state view is displayed only when the user has one card left in the wallet, removes the card from the card detail page, and then goes back to the wallet view.

  • The lock screen shows the wallet icon.

Empty state view in the Quick Access Wallet

Figure 8. Empty state view in the Quick Access Wallet UI.

Non-zero state

  • If the wallet app provides one or more cards, the tile in the shade appears as shown in Figure 9.

    Example tile in the shade when wallet app has one or more cards

    Figure 9. Example tile in the shade when wallet app has one or more cards.

  • Clicking on the tile shows a card carousel.

  • The lock screen shows a button that opens the Quick Access Wallet.

    Quick Access Wallet UI with a card displayed

    Figure 10. Quick Access Wallet UI with a card displayed.

  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking on a displayed card opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains one entry: open the lock screen settings so that the user can change the Show wallet option.

Lock state tests

  • If the phone is locked, the wallet is visible on the quick settings shade, with a description of Add a card if no card exists in the default payment app, or unlock to use if cards exist in the default payment app.
  • If the phone is locked, wallet visibility on the lock screen is controlled by the Secure.LOCKSCREEN_SHOW_WALLET setting, which is controlled in Settings.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is false , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and cards exist in the default NFC payment app, the wallet is displayed on the lock screen.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being requeried, which might result in different card content.

Accessibility tests

  • Talkback users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with Talkback enabled selects each card in turn. Talkback users can select and use an NFC payment method at an NFC payment terminal.

Manual tests for Android 11

Testing the core features of the Quick Access Wallet requires an NFC payment terminal (real or fake) and an NFC payment app that implements QuickAccessWalletService (wallet app). Core features that must be tested include availability, zero state, card selection, and lock screen behavior.

Availability

  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app supports the feature, the Quick Access Wallet is accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app supports the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.

Zero state

  • If QuickAccessWalletService is enabled and exported but doesn't provide any cards, the Quick Access Wallet UI displays the empty state view.
  • Clicking the empty state view opens the wallet app.

    Empty state view in the Quick Access Wallet UI
    Figure 11. Empty state view in the Quick Access Wallet UI.

Non-zero state

  • If the wallet app provides one or more cards, the cards are displayed in the Quick Access Wallet UI.

    Quick Access Wallet UI with a card displayed
    Figure 12. Quick Access Wallet UI with a card displayed.
  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking a displayed card dismisses the wallet view and opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains two entries: one that opens the wallet app and one that opens the Show cards & passes screen in Settings.

Lock state tests

  • If the phone is locked, wallet visibility is controlled by the Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT setting, which can be controlled in Settings.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is false , the wallet isn't displayed.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is true , the wallet is displayed.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being re-queried, which might result in different card content.

Accessibility tests

  • TalkBack users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with TalkBack enabled selects each card in turn. TalkBack users can select and use an NFC payment method at an NFC payment terminal.
,

The Quick Access Wallet feature, available from Android 11, allows the user to access payment cards and relevant passes directly from the power menu. Major use cases include selecting the appropriate payment method before performing a transaction at an NFC terminal and quickly accessing flights and other passes for upcoming events.

In Android 12 or higher, the Quick Access Wallet feature is available from the shade as shown in Figure 1 and Figure 2.

Quick Access Wallet feature in shade
Figure 1. Quick Access Wallet feature (device locked).
Quick Access Wallet feature in shade
Figure 2. Quick Access Wallet feature (device unlocked).

In Android 11, the feature is available from the power menu as shown in Figure 3.

Quick Access Wallet feature in power menu
Figure 3. Quick Access Wallet feature in power menu.

Requirements

Your device must have NFC to use the Quick Access Wallet feature. The feature binds to QuickAccessWalletService of the default NFC payment app, which means that the device must also support NFC host-based card emulation (HCE) .

Feature overview

There are two parts to the Quick Access Wallet: the Quick Access Wallet UI and the Quick Access Wallet card provider.

In Android 12 or higher, the Wallet UI runs in System UI and is located in frameworks/base/packages/SystemUI/src/com/android/systemui/wallet . In Android 11, the Wallet UI, which is located in platform/packages/apps/QuickAccessWallet , must be installed and whitelisted.

The Quick Access Wallet card provider is the default NFC payment app. Users can have multiple NFC payment apps installed simultaneously, but only the default NFC payment app can show cards on the power menu. You can specify which NFC payment app is set as the default initially, but users can select a different app in Settings. If only one NFC payment app is installed, it becomes the default automatically (see CardEmulationManager ).

執行

To provide cards to the Quick Access Wallet UI, NFC payment apps must implement QuickAccessWalletService . Payment apps must include a manifest entry advertising the service.

To ensure that only the System UI can bind to QuickAccessWalletService , the NFC payment app must require the android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission. Requiring this permission ensures that only the System UI can bind to 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>

Additional information about the wallet is included in the linked XML file:

<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"/>

Next, the payment app must implement 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
    }
}

If HostApduService starts to handle an NFC transaction and, as a consequence, starts an activity to display the progress and outcome of the payment, it should also try to get a reference to the bound QuickAccessWalletService and call QuickAccessWalletService#sendEvent with an event type of TYPE_NFC_PAYMENT_STARTED . This causes the Quick Access Wallet UI to be dismissed, thus allowing the user an unobstructed view of the payment activity.

For additional documentation on implementing QuickAccessWalletService , see QuickAccessWalletService and the TestQuickAccessWalletService CTS test.

Enabling Quick Access Wallet UI in Android 11

To configure the Quick Access Wallet to be available from the power menu in Android 11, include the QuickAccessWallet target in the build and enable the globalactions.wallet plugin by adding the line in bold in the code sample below to the overlay/frameworks/base/packages/SystemUI/res/values/config.xml file.

<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>

Specify the default NFC payment app in the settings configuration file using def_nfc_payment_component .

The default NFC payment app must expose QuickAccessWalletService to provide cards to the Quick Access Wallet. If the default NFC payment app doesn't export this service, the wallet UI is hidden.

QuickAccessWalletService implementation details

QuickAccessWalletService has three abstract methods that must be implemented: onWalletCardsRequested , onWalletCardSelected , and onWalletDismissed . The sequence diagram below illustrates a call sequence when the Quick Access Wallet is viewed immediately preceding an NFC payment.

Quick Access Wallet sequence diagram

Example call sequence when Quick Access Wallet is viewed
Figure 4. Example call sequence when Quick Access Wallet is viewed.

Not all views of the Quick Access Wallet are followed by an NFC payment, but Figure 4 above illustrates all of the capabilities of QuickAccessWalletService . In this example, the Quick Access Wallet card provider implements the elements outlined in blue. It's assumed that payment cards are stored on the device in a database and are accessed through an interface named PaymentCardManager . It's further assumed that an activity called PaymentActivity displays the result of an NFC payment. The flow proceeds as follows:

  1. The user performs a gesture to bring up the Quick Access Wallet.
  2. The Quick Access Wallet UI (part of System UI) checks the package manager to see if the default NFC payment app exports QuickAccessWalletService .

    • If the service isn't exported, the Quick Access Wallet isn't displayed.
  3. The Quick Access Wallet UI binds to the QuickAccessWalletService and calls onWalletCardsRequested . This method takes a request object containing data about the number and size of the cards that can be provided and a callback. The callback can be called from a background thread.

  4. QuickAccessWalletService computes the cards that it wants to show, then calls the onSuccess method on the provided callback. It's recommended that the service perform these actions on a background thread.

  5. As soon as the cards are displayed, the System UI notifies QuickAccessWalletService that the first card has been selected by calling onWalletCardSelected .

    • onWalletCardSelected is called every time the user selects a new card.
    • onWalletCardSelected might be called even if the currently selected card hasn't changed.
  6. When the user dismisses the Quick Access Wallet, the System UI notifies QuickAccessWalletService by calling onWalletDismissed .

In the above example, the user brings the phone into range of an NFC payment terminal while the wallet is being displayed. A key component of handling NFC payments is HostApduService , which must be implemented to process APDUs provided by the NFC reader (for more information, see Host-based card emulation ). It's assumed that the payment app starts an activity to display the progress and result of the interaction with the NFC terminal. However, the Quick Access Wallet UI is displayed on top of the app window, meaning that the payment activity is obscured by the Quick Access Wallet UI. To rectify this, the app must notify the System UI that the Quick Access Wallet UI should be dismissed. It can do so by getting a reference to the bound QuickAccessWalletService and calling sendWalletServiceEvent with the event type TYPE_NFC_PAYMENT_STARTED .

QuickAccessWalletService sample implementation

/** 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();
    });
  }
}

For further details about QuickAccessWalletService , see QuickAccessWalletService API reference .

Permissions

The manifest entry for QuickAccessWalletService must require the android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission introduced in Android 11. This is a signature-level permission held by the System UI, which means that only the System UI process can bind to implementations of QuickAccessWalletService . Be aware that side-loaded apps can claim this permission and get full access to QuickAccessWalletService data on devices running Android 10 or lower. To prevent this, it's recommended that the service check the build version in onCreate and enable the service only on devices running Android 11 and higher. No other app permissions are required beyond those necessary to provide host card emulation payment services.

If the default NFC payment app doesn't implement or export QuickAccessWalletService , the Quick Access Wallet UI isn't displayed.

Settings in Android 12

To enable or disable the Quick Access Wallet from the lock screen, users can use the Show wallet toggle in Settings > Display > Lock screen . To disable the wallet in the shade, users must manually edit it in the quick settings shade.

Toggle to enable or disable wallet from lock screen

Figure 5. Show wallet toggle in the Lock screen page in Settings.

Settings in Android 11

Users can turn off the Quick Access Wallet feature from the Settings app. The settings page is found in Settings > System > Gestures > Cards & passes .

Settings page to enable or disable the Quick Access Wallet feature
Figure 6. Settings page to enable or disable the Quick Access Wallet feature.

定制

Adding Quick Access Wallet view to another location in System UI

The Quick Access Wallet UI is built as a system plugin . Although the AOSP implementation makes use of it in GlobalActionsDialog (shown on long power press), you can move the feature behind a different gesture as long as you maintain the contract specified by the plugin interface.

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);
  }
}

The Quick Access Wallet UI implements GlobalActionsPanelPlugin and PanelViewController . GlobalActionsDialog gets an instance of the wallet plugin by using com.android.systemui.Dependency :

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

After checking that the plugin is non-null and that the PanelViewController returned by onPanelShown is non-null, the dialog attaches the View provided by getPanelContent to its own View and provides appropriate callbacks for system events.

// 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();

To remove the Quick Access Wallet from the power menu, omit the QuickAccessWallet target from the system build. To remove the Quick Access Wallet from the power menu but add it to a different System UI provided view, include the build target and remove references to the GlobalActionsPanelPlugin from GlobalActionsImpl .

Setting default configurations

Android 12

In Android 12 or higher, the Quick Access Wallet is always visible in the quick settings shade. Visibility of the Quick Access Wallet in the lock screen is gated by the following secure setting: LOCKSCREEN_SHOW_WALLET . This setting controls whether the Quick Access Wallet icon is shown on the bottom right of the lock screen. This setting is set to true by default, but can be turned off by the user in Settings > Display > Lock screen > Show wallet .

Android 11

In Android 11, Quick Access Wallet visibility is gated by two secure settings: GLOBAL_ACTIONS_PANEL_ENABLED and GLOBAL_ACTIONS_PANEL_AVAILABLE . The AVAILABLE setting controls whether the feature can be turned on and off in Settings. This setting is set to true by WalletPluginService . If QuickAccessWallet isn't included in the build, then the setting remains false . The ENABLED setting is set to true by default in the same place, but can be turned off by the user in Settings. To change the default behavior, modify WalletPluginService#enableFeatureInSettings .

驗證

To validate your implementation of the Quick Access Wallet, run CTS and manual tests. Changes to the plugin should also exercise the included robolectric tests .

CTS tests

Run the CTS tests located at cts/tests/quickaccesswallet .

Manual tests for Android 12

Testing the core features of the Quick Access Wallet requires an NFC payment terminal (real or fake) and an NFC payment app that implements QuickAccessWalletService (wallet app). Core features that must be tested include: availability, zero state, card selection, and lock screen behavior.

Availability

  • If the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible in neither the quick settings nor the lock screen.
  • If the default NFC payment app supports the feature, the Quick Access Wallet is accessible in the quick settings shade.
  • If the default NFC payment app supports the feature and if the LOCKSCREEN_SHOW_WALLET setting is true , the Quick Access Wallet is accessible on the lock screen.
  • If the default NFC payment app supports the feature and if the LOCKSCREEN_SHOW_WALLET setting is false , the Quick Access Wallet isn't accessible on the lock screen.

Zero state

  • If QuickAccessWalletService is enabled and exported but doesn't provide any cards, the tile in the Shade appears as shown in the example in Figure 7. Clicking on the tile opens the default NFC payment app.

    Example tile in the shade showing default NFC payment app

    Figure 7. Example tile in the shade showing default NFC payment app.

  • Clicking on the empty state view as shown in Figure 8 opens the default NFC payment app. This empty state view is displayed only when the user has one card left in the wallet, removes the card from the card detail page, and then goes back to the wallet view.

  • The lock screen shows the wallet icon.

Empty state view in the Quick Access Wallet

Figure 8. Empty state view in the Quick Access Wallet UI.

Non-zero state

  • If the wallet app provides one or more cards, the tile in the shade appears as shown in Figure 9.

    Example tile in the shade when wallet app has one or more cards

    Figure 9. Example tile in the shade when wallet app has one or more cards.

  • Clicking on the tile shows a card carousel.

  • The lock screen shows a button that opens the Quick Access Wallet.

    Quick Access Wallet UI with a card displayed

    Figure 10. Quick Access Wallet UI with a card displayed.

  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking on a displayed card opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains one entry: open the lock screen settings so that the user can change the Show wallet option.

Lock state tests

  • If the phone is locked, the wallet is visible on the quick settings shade, with a description of Add a card if no card exists in the default payment app, or unlock to use if cards exist in the default payment app.
  • If the phone is locked, wallet visibility on the lock screen is controlled by the Secure.LOCKSCREEN_SHOW_WALLET setting, which is controlled in Settings.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is false , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen.
  • If the phone is locked, LOCKSCREEN_SHOW_WALLET is true , and cards exist in the default NFC payment app, the wallet is displayed on the lock screen.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being requeried, which might result in different card content.

Accessibility tests

  • Talkback users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with Talkback enabled selects each card in turn. Talkback users can select and use an NFC payment method at an NFC payment terminal.

Manual tests for Android 11

Testing the core features of the Quick Access Wallet requires an NFC payment terminal (real or fake) and an NFC payment app that implements QuickAccessWalletService (wallet app). Core features that must be tested include availability, zero state, card selection, and lock screen behavior.

Availability

  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app supports the feature, the Quick Access Wallet is accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app supports the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is true and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.
  • If the GLOBAL_ACTIONS_PANEL_ENABLED setting is false and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.

Zero state

  • If QuickAccessWalletService is enabled and exported but doesn't provide any cards, the Quick Access Wallet UI displays the empty state view.
  • Clicking the empty state view opens the wallet app.

    Empty state view in the Quick Access Wallet UI
    Figure 11. Empty state view in the Quick Access Wallet UI.

Non-zero state

  • If the wallet app provides one or more cards, the cards are displayed in the Quick Access Wallet UI.

    Quick Access Wallet UI with a card displayed
    Figure 12. Quick Access Wallet UI with a card displayed.
  • If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.

  • Clicking a displayed card dismisses the wallet view and opens the detailed activity for that card.

  • If multiple cards are provided by QuickAccessWalletService , the user is able to swipe between cards.

  • The overflow menu contains two entries: one that opens the wallet app and one that opens the Show cards & passes screen in Settings.

Lock state tests

  • If the phone is locked, wallet visibility is controlled by the Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT setting, which can be controlled in Settings.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is false , the wallet isn't displayed.
  • If the phone is locked and POWER_MENU_LOCK_SHOW_CONTENT is true , the wallet is displayed.
  • Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being re-queried, which might result in different card content.

Accessibility tests

  • TalkBack users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
  • Swiping left and right with TalkBack enabled selects each card in turn. TalkBack users can select and use an NFC payment method at an NFC payment terminal.