Android 11 中提供的快速存取錢包功能可讓用戶直接從電源選單存取支付卡和相關通行證。主要用例包括在 NFC 終端執行交易之前選擇適當的付款方式,以及快速存取即將舉行的活動的航班和其他通行證。
在 Android 12 或更高版本中,可以從陰影中使用快速存取錢包功能,如圖 1 和圖 2 所示。
在 Android 11 中,可以從電源選單使用該功能,如圖 3 所示。
要求
您的裝置必須具有 NFC 才能使用快速存取錢包功能。此功能綁定到預設 NFC 支付應用程式的QuickAccessWalletService
,這表示設備還必須支援 NFC基於主機的卡模擬 (HCE) 。
功能概述
快速存取錢包有兩個部分:快速存取錢包 UI 和快速存取錢包卡提供者。
在 Android 12 或更高版本中,錢包 UI 在系統 UI 中運行,位於frameworks/base/packages/SystemUI/src/com/android/systemui/wallet
中。在 Android 11 中,必須安裝位於platform/packages/apps/QuickAccessWallet
中的錢包 UI 並將其列入白名單。
Quick Access Wallet 卡提供者是預設的 NFC 支付應用程式。用戶可以同時安裝多個 NFC 支付應用程序,但只有預設的NFC 支付應用程式可以在電源選單上顯示卡片。您可以指定最初將哪個 NFC 支付應用程式設定為預設應用程序,但使用者可以在「設定」中選擇不同的應用程式。如果僅安裝一個 NFC 支付應用程序,它將自動成為預設應用程式(請參閱CardEmulationManager
)。
執行
若要向快速存取錢包 UI 提供卡,NFC 支付應用程式必須實作QuickAccessWalletService
。支付應用程式必須包含宣傳該服務的清單條目。
為了確保只有系統 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
引用,並使用事件類型QuickAccessWalletService#sendEvent
呼叫 QuickAccessWalletService# TYPE_NFC_PAYMENT_STARTED
。這會導致快速存取錢包 UI 被關閉,使用戶能夠暢通無阻地查看支付活動。
有關實作QuickAccessWalletService
其他文檔,請參閱QuickAccessWalletService
和TestQuickAccessWalletService
CTS 測試。
在 Android 11 中啟用快速存取錢包 UI
若要將快速存取錢包配置為可從 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
有必須實作的三個抽象方法: onWalletCardsRequested
、 onWalletCardSelected
和onWalletDismissed
。下面的序列圖說明了在 NFC 付款之前查看快速存取錢包時的呼叫序列。
並非 Quick Access Wallet 的所有視圖都會跟隨 NFC 支付,但上面的圖 4 說明了QuickAccessWalletService
的所有功能。在此範例中,快速存取錢包卡提供者實現了藍色概述的元素。假設支付卡儲存在裝置上的資料庫中,並透過名為PaymentCardManager
介面進行存取。進一步假設名為PaymentActivity
的活動顯示 NFC 支付的結果。流程如下:
- 用戶執行手勢以調出快速存取錢包。
快速存取錢包 UI(系統 UI 的一部分)檢查包管理器以查看預設 NFC 支付應用程式是否匯出
QuickAccessWalletService
。- 如果未匯出服務,則不會顯示快速存取錢包。
快速存取錢包 UI 綁定到
QuickAccessWalletService
並呼叫onWalletCardsRequested
。此方法採用一個請求對象,其中包含有關可提供的卡片數量和大小的資料以及回呼。可以從後台執行緒呼叫回調。QuickAccessWalletService
計算它想要顯示的卡片,然後在提供的回呼上呼叫onSuccess
方法。建議服務在後台執行緒上執行這些操作。卡片一顯示,系統 UI 就會通知
QuickAccessWalletService
已透過呼叫onWalletCardSelected
選擇第一張卡。- 每次使用者選擇新卡時都會呼叫
onWalletCardSelected
。 - 即使目前選取的卡片未更改,也可能會呼叫
onWalletCardSelected
。
- 每次使用者選擇新卡時都會呼叫
當用戶關閉快速存取錢包時,系統 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
的清單項目必須需要 Android 11 中引入的android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE
權限。這是系統 UI 持有的簽章級權限,這意味著只有系統 UI 程序才能綁定到QuickAccessWalletService
的實作。請注意,側面載入的應用程式可以聲明此權限,並在運行 Android 10 或更低版本的裝置上取得對QuickAccessWalletService
資料的完全存取權。為了防止這種情況,建議該服務在onCreate
中檢查建置版本,並僅在運行 Android 11 及更高版本的裝置上啟用該服務。除了提供主機卡模擬支付服務所需的權限之外,不需要其他應用程式權限。
如果預設 NFC 支付應用程式未實作或匯出QuickAccessWalletService
,則不會顯示快速存取錢包 UI。
Android 12 中的設定
若要從鎖定畫面啟用或停用快速存取錢包,使用者可以使用「設定」 > 「顯示」 > 「鎖定畫面」中的「顯示錢包」開關。要停用陰影中的錢包,用戶必須在快速設定陰影中手動編輯它。
圖 5.在「設定」的鎖定畫面頁面中顯示錢包切換。
Android 11 中的設置
用戶可以從「設定」應用程式關閉「快速存取錢包」功能。設定頁面位於設定>系統>手勢>卡片和通行證。
客製化
將快速存取錢包視圖新增至系統 UI 中的另一個位置
快速存取錢包 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實現GlobalActionsPanelPlugin
和PanelViewController
。 GlobalActionsDialog
使用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
,但使用者可以在設定>顯示>鎖定畫面>顯示錢包中關閉。
安卓11
在 Android 11 中,快速存取錢包的可見性由兩個安全設定控制: GLOBAL_ACTIONS_PANEL_ENABLED
和GLOBAL_ACTIONS_PANEL_AVAILABLE
。 AVAILABLE
設定控制是否可以在「設定」中開啟和關閉該功能。此設定由WalletPluginService
設定為true
。如果QuickAccessWallet
未包含在建置中,則該設定仍為false
。 ENABLED
設定在相同位置預設為true
,但使用者可以在「設定」中將其關閉。若要變更預設行為,請修改WalletPluginService#enableFeatureInSettings
。
驗證
若要驗證快速存取錢包的實施,請執行 CTS 和手動測試。對插件的更改也應該執行包含的robolectric 測試。
CTS測試
執行位於cts/tests/quickaccesswallet
的 CTS 測試。
Android 12 的手動測試
測試Quick Access Wallet的核心功能需要一個NFC支付終端(真或假)和一個實現QuickAccessWalletService
的NFC支付應用程式(錢包應用程式)。必須測試的核心功能包括:可用性、零狀態、卡片選擇和鎖定螢幕行為。
可用性
- 如果預設的 NFC 支付應用程式不支援該功能,則無論是在快速設定還是鎖定畫面中都無法存取快速存取錢包。
- 如果預設 NFC 支付應用程式支援該功能,則可以在快速設定陰影中存取快速存取錢包。
- 如果預設 NFC 支付應用程式支援該功能且
LOCKSCREEN_SHOW_WALLET
設定為true
,則可以在鎖定畫面上存取快速存取錢包。 - 如果預設 NFC 支付應用程式支援該功能,且
LOCKSCREEN_SHOW_WALLET
設定為false
,則無法在鎖定畫面上存取快速存取錢包。
零狀態
如果啟用並匯出
QuickAccessWalletService
,但不提供任何卡,則陰影中的磁貼將顯示如圖 7 中的範例所示。按一下磁貼將開啟預設的 NFC 支付應用程式。圖 7.陰影中顯示預設 NFC 支付應用程式的範例圖塊。
點擊如圖 8 所示的空狀態視圖將開啟預設的 NFC 支付應用程式。只有當使用者錢包中剩餘一張卡片、從卡片詳細資料頁面刪除該卡,然後返回錢包視圖時,才會顯示此空狀態視圖。
鎖定畫面顯示錢包圖示。
圖 8.快速存取錢包 UI 中的空狀態視圖。
非零狀態
如果錢包應用程式提供一張或多張卡,則陰影中的圖塊將顯示如圖 9 所示。
圖 9.當錢包應用程式有一張或多張卡片時,陰影中的範例圖塊。
點選圖塊會顯示卡片輪播。
鎖定畫面顯示一個打開快速存取錢包的按鈕。
圖 10.顯示卡片的快速存取錢包 UI。
如果顯示的卡片代表 NFC 支付方式,則將手機靠近 NFC 支付終端會導致使用該支付方式並關閉錢包視圖。
按一下顯示的卡片可開啟該卡片的詳細活動。
如果
QuickAccessWalletService
提供多張卡,使用者可以在卡片之間滑動。溢出選單包含一項:打開鎖定螢幕設置,以便用戶可以更改“顯示錢包”選項。
鎖定狀態測試
- 如果手機被鎖定,則錢包在快速設定陰影中可見,並帶有「如果預設支付應用程式中不存在卡片則新增卡片」的說明,或者如果預設支付應用程式中存在卡片則解鎖以使用。
- 如果手機被鎖定,錢包在鎖定螢幕上的可見性由
Secure.LOCKSCREEN_SHOW_WALLET
設定控制,該設定在「設定」中控制。 - 如果手機處於鎖定狀態,
LOCKSCREEN_SHOW_WALLET
為false
,且預設 NFC 支付應用程式中不存在卡,則錢包不會顯示在鎖定畫面上。 - 如果手機處於鎖定狀態,
LOCKSCREEN_SHOW_WALLET
為true
,且預設 NFC 支付應用程式中不存在卡,則錢包不會顯示在鎖定畫面上。 - 如果手機處於鎖定狀態,
LOCKSCREEN_SHOW_WALLET
為true
,且卡片存在於預設 NFC 支付應用程式中,則錢包會顯示在鎖定畫面上。 - 當錢包顯示在鎖定螢幕上時解鎖手機會導致重新查詢卡,這可能會導致卡片內容不同。
輔助功能測試
- Talkback 用戶可以透過左右滑動並收聽卡片的內容描述來導航錢包視圖。
- 在啟用 Talkback 的情況下左右滑動可依序選擇每張卡片。 Talkback用戶可以在NFC支付終端選擇並使用NFC支付方式。
Android 11 的手動測試
測試Quick Access Wallet的核心功能需要一個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 中。
如果顯示的卡片代表 NFC 支付方式,則將手機靠近 NFC 支付終端會導致使用該支付方式並關閉錢包視圖。
點擊顯示的卡將關閉錢包視圖並打開該卡的詳細活動。
如果
QuickAccessWalletService
提供多張卡,使用者可以在卡片之間滑動。溢出選單包含兩個條目:一個用於開啟錢包應用程序,另一個用於在「設定」中開啟「顯示卡和通行證」畫面。
鎖定狀態測試
- 如果手機被鎖定,錢包可見性由
Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT
設定控制,該設定可以在「設定」中控制。 - 如果手機已鎖定且
POWER_MENU_LOCK_SHOW_CONTENT
為false
,則不會顯示錢包。 - 如果手機已鎖定且
POWER_MENU_LOCK_SHOW_CONTENT
為true
,則會顯示錢包。 - 當錢包顯示在鎖定螢幕上時解鎖手機會導致卡片被重新查詢,這可能會導致卡片內容不同。
輔助功能測試
- TalkBack 用戶可以透過左右滑動並收聽卡片的內容描述來導覽錢包視圖。
- 啟用 TalkBack 時左右滑動可依序選擇每張卡片。 TalkBack用戶可以在NFC支付終端選擇並使用NFC支付方式。