快速存取錢包功能可讓使用者直接從電源選單存取付款卡和相關票證,這項功能可在 Android 11 以上版本使用。主要用途包括在 NFC 終端機上執行交易前,選取適當的付款方式,以及快速存取即將到來的航班和其他通行證。
在 Android 12 以上版本中,您可以透過遮罩使用快速存取錢包功能,如圖 1 和圖 2 所示。
在 Android 11 中,您可以透過電源選單使用這項功能,如圖 3 所示。
需求條件
裝置必須支援 NFC,才能使用快速存取錢包功能。這項功能會繫結至預設 NFC 付款應用程式的 QuickAccessWalletService
,也就是說裝置也必須支援 NFC 主機卡模擬 (HCE)。
功能總覽
快速存取電子錢包包含兩個部分:快速存取電子錢包使用者介面和快速存取電子錢包卡片供應器。
在 Android 12 以上版本中,錢包 UI 會在系統 UI 中執行,並位於 frameworks/base/packages/SystemUI/src/com/android/systemui/wallet
中。在 Android 11 中,必須安裝位於 platform/packages/apps/QuickAccessWallet
中的錢包 UI,並將其列入許可清單。
快速存取錢包卡片供應商是預設的 NFC 付款應用程式。使用者可以同時安裝多個 NFC 付款應用程式,但只有預設 NFC 付款應用程式可在電源選單中顯示卡片。您可以指定哪個 NFC 付款應用程式設為預設,但使用者可以在「設定」中選取其他應用程式。如果只安裝一個 NFC 付款應用程式,系統會自動將其設為預設值 (請參閱 CardEmulationManager
)。
實作
如要將卡片提供給快速存取錢包 UI,NFC 支付應用程式必須實作 QuickAccessWalletService
。付款應用程式必須包含宣傳服務的資訊清單項目。
為確保只有系統使用者介面可以繫結至 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
。這會導致快速存取錢包 UI 關閉,讓使用者可以清楚查看付款活動。
如需有關實作 QuickAccessWalletService
的其他說明文件,請參閱 QuickAccessWalletService
和 TestQuickAccessWalletService
CTS 測試。
在 Android 11 中啟用快速存取電子錢包 UI
如要設定可透過 Android 11 中的電源選單存取的快速存取錢包,請在建構中加入 QuickAccessWallet
目標,並將下方程式碼範例中粗體顯示的行新增至 overlay/frameworks/base/packages/SystemUI/res/values/config.xml
檔案,啟用 globalactions.wallet
外掛程式。
<resources> ... <!-- SystemUI Plugins that can be loaded on user builds. --> <string-array name="config_pluginWhitelist" translatable="false"> <item>com.android.systemui</item> <item>com.android.systemui.plugin.globalactions.wallet</item> </string-array> </resources>
使用 def_nfc_payment_component
,在設定檔設定檔中指定預設 NFC 付款應用程式。
預設的 NFC 付款應用程式必須公開 QuickAccessWalletService
,才能將卡片提供給快速存取錢包。如果預設 NFC 付款應用程式未匯出這項服務,錢包 UI 就會隱藏。
QuickAccessWalletService 實作詳情
QuickAccessWalletService
有三個必須實作抽象方法:onWalletCardsRequested
、onWalletCardSelected
和 onWalletDismissed
。下方的序列圖說明在使用者即將透過 NFC 付款前,快速存取錢包的顯示畫面會顯示哪些呼叫。
並非所有快速存取錢包的畫面都會隨後顯示 NFC 付款畫面,但上圖 4 顯示了 QuickAccessWalletService
的所有功能。在這個範例中,快速存取錢包卡片供應器會實作以藍色標示的元素。假設付款卡儲存在裝置的資料庫中,並透過名為 PaymentCardManager
的介面存取。我們進一步假設名為 PaymentActivity
的活動會顯示 NFC 付款結果。流程如下:
- 使用者執行手勢,開啟快速存取錢包。
快速存取錢包 UI (系統 UI 的一部分) 會檢查套件管理員,瞭解預設 NFC 付款應用程式是否匯出
QuickAccessWalletService
。- 如果未匯出服務,系統就不會顯示快速存取錢包。
快速存取電子錢包 UI 會繫結至
QuickAccessWalletService
並呼叫onWalletCardsRequested
。這個方法會接收要求物件,其中包含可提供的卡片數量和大小相關資料,以及回呼。回呼可從背景執行緒呼叫。QuickAccessWalletService
會計算想要顯示的資訊卡,然後在提供的回呼上呼叫onSuccess
方法。建議服務在背景執行緒上執行這些動作。只要顯示卡片,系統 UI 就會呼叫
onWalletCardSelected
,通知QuickAccessWalletService
已選取第一張卡片。- 每當使用者選取新卡片時,系統就會呼叫
onWalletCardSelected
。 - 即使目前選取的卡片未變更,系統仍可能會呼叫
onWalletCardSelected
。
- 每當使用者選取新卡片時,系統就會呼叫
當使用者關閉快速存取錢包時,系統 UI 會呼叫
onWalletDismissed
通知QuickAccessWalletService
。
在上述範例中,使用者在顯示錢包時,將手機靠近 NFC 支付終端機的範圍。處理 NFC 付款的關鍵元件是 HostApduService
,必須實作此元件才能處理 NFC 讀取器提供的 APDU (詳情請參閱「以主機為基礎的卡片模擬」)。假設付款應用程式會啟動活動,以顯示與 NFC 終端機互動的進度和結果。不過,「快速存取錢包」使用者介面會顯示在應用程式視窗上方,也就是說,「快速存取錢包」使用者介面會遮住付款活動。為修正這個問題,應用程式必須通知系統 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
,系統就不會顯示「快速存取錢包」使用者介面。
Android 12 中的設定
如要啟用或停用螢幕鎖定畫面的快速存取錢包功能,使用者可以依序前往「設定」 >「顯示」 >「螢幕鎖定畫面」,然後使用「顯示錢包」切換按鈕。如要在快速設定面板中停用錢包,使用者必須在快速設定面板中手動編輯。
圖 5. 在「設定」的「螢幕鎖定」頁面中顯示錢包切換按鈕。
Android 11 中的設定
使用者可以透過「設定」應用程式關閉「快速存取錢包」功能。如要前往設定頁面,請依序前往「設定」>「系統」>「手勢」>「卡片與通行證」。
自訂
將快速存取錢包檢視畫面新增至系統 UI 中的其他位置
快速存取錢包 UI 是以系統外掛程式打造而成。雖然 Android 開放原始碼計畫實作項目會在 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
的參照。
設定預設設定
Android 12
在 Android 12 以上版本中,「快速存取錢包」一律會顯示在快速設定面板中。鎖定畫面上是否顯示「快速存取」錢包,取決於下列安全設定:LOCKSCREEN_SHOW_WALLET
。這項設定可控管是否要在螢幕鎖定畫面的右下方顯示快速存取錢包圖示。這項設定預設為 true
,但使用者可以依序前往「設定」 >「顯示」 >「螢幕鎖定」 >「顯示錢包」,將其關閉。
Android 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 手動測試
如要測試快速存取錢包的核心功能,您需要一個 NFC 支付終端機 (真實或虛擬),以及實作 QuickAccessWalletService
(錢包應用程式) 的 NFC 支付應用程式。必須測試的核心功能包括:可用性、零狀態、資訊卡選取和螢幕鎖定行為。
適用地區
- 如果預設 NFC 付款應用程式不支援這項功能,在快速設定或鎖定畫面中都無法存取快速存取錢包。
- 如果預設的 NFC 付款應用程式支援這項功能,您可以在快速設定面板中存取「快速存取錢包」功能。
- 如果預設的 NFC 付款應用程式支援這項功能,且
LOCKSCREEN_SHOW_WALLET
設定為true
,您就能在鎖定畫面上使用快速存取錢包。 - 如果預設的 NFC 付款應用程式支援這項功能,且
LOCKSCREEN_SHOW_WALLET
設定為false
,就無法在鎖定畫面上使用快速存取錢包。
零狀態
如果
QuickAccessWalletService
已啟用並匯出,但並未提供任何資訊卡,陰影中的圖塊會如圖 7 中的範例所示。點選資訊方塊即可開啟預設的 NFC 付款應用程式。圖 7. 範例:通知欄中的資訊方塊,顯示預設的 NFC 付款應用程式。
按一下圖 8 所示的空白狀態檢視畫面,即可開啟預設的 NFC 付款應用程式。只有在使用者錢包中仍有一張卡片、從卡片詳細資料頁面移除卡片,然後返回錢包檢視畫面時,才會顯示這個空白狀態檢視畫面。
螢幕鎖定畫面會顯示錢包圖示。
圖 8. 「快速存取錢包」使用者介面的空白狀態檢視畫面。
非零狀態
如果錢包應用程式提供一或多張卡片,則陰影中的圖塊會顯示如圖 9 所示。
圖 9.錢包應用程式有一或多張卡片時,遮罩中的資訊方塊範例。
點選資訊方塊即可顯示資訊卡輪轉介面。
鎖定畫面會顯示可開啟快速存取錢包的按鈕。
圖 10. 顯示卡片的快速存取錢包 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 手動測試
如要測試快速存取錢包的核心功能,您需要一個 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 付款方式。