Merge "Improve QuickAccessWallet documentation and client."
This commit is contained in:
@@ -43967,7 +43967,7 @@ package android.service.notification {
|
||||
|
||||
package android.service.quickaccesswallet {
|
||||
|
||||
public final class GetWalletCardsCallback {
|
||||
public interface GetWalletCardsCallback {
|
||||
method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
|
||||
method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
|
||||
}
|
||||
@@ -44008,7 +44008,6 @@ package android.service.quickaccesswallet {
|
||||
method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback);
|
||||
method public abstract void onWalletDismissed();
|
||||
method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
|
||||
field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET";
|
||||
field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET";
|
||||
field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
|
||||
field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService";
|
||||
|
||||
@@ -2976,6 +2976,9 @@ package android.provider {
|
||||
field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
|
||||
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
|
||||
field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
|
||||
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
|
||||
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
|
||||
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
|
||||
field public static final String NOTIFICATION_BADGING = "notification_badging";
|
||||
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
|
||||
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
|
||||
@@ -3399,6 +3402,34 @@ package android.service.notification {
|
||||
|
||||
}
|
||||
|
||||
package android.service.quickaccesswallet {
|
||||
|
||||
public interface QuickAccessWalletClient {
|
||||
method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
|
||||
method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
|
||||
method @Nullable public android.content.Intent createWalletIntent();
|
||||
method @Nullable public android.content.Intent createWalletSettingsIntent();
|
||||
method public void disconnect();
|
||||
method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
|
||||
method public boolean isWalletFeatureAvailable();
|
||||
method public boolean isWalletFeatureAvailableWhenDeviceLocked();
|
||||
method public boolean isWalletServiceAvailable();
|
||||
method public void notifyWalletDismissed();
|
||||
method public void removeWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
|
||||
method public void selectWalletCard(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
|
||||
}
|
||||
|
||||
public static interface QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
|
||||
method public void onWalletCardRetrievalError(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
|
||||
method public void onWalletCardsRetrieved(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
|
||||
}
|
||||
|
||||
public static interface QuickAccessWalletClient.WalletServiceEventListener {
|
||||
method public void onWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.quicksettings {
|
||||
|
||||
public class TileService extends android.app.Service {
|
||||
|
||||
@@ -6462,6 +6462,7 @@ public final class Settings {
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
|
||||
"lock_screen_allow_private_notifications";
|
||||
|
||||
@@ -7880,6 +7881,7 @@ public final class Settings {
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
@TestApi
|
||||
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
|
||||
|
||||
/**
|
||||
@@ -8082,6 +8084,7 @@ public final class Settings {
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
|
||||
"lock_screen_show_notifications";
|
||||
|
||||
|
||||
@@ -17,28 +17,11 @@
|
||||
package android.service.quickaccesswallet;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
|
||||
*/
|
||||
public final class GetWalletCardsCallback {
|
||||
|
||||
private static final String TAG = "QAWalletCallback";
|
||||
|
||||
private final IQuickAccessWalletServiceCallbacks mCallback;
|
||||
private final Handler mHandler;
|
||||
private boolean mCalled;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) {
|
||||
mCallback = callback;
|
||||
mHandler = handler;
|
||||
}
|
||||
public interface GetWalletCardsCallback {
|
||||
|
||||
/**
|
||||
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
|
||||
@@ -46,11 +29,10 @@ public final class GetWalletCardsCallback {
|
||||
*
|
||||
* @param response The response contains the list of {@link WalletCard walletCards} to be shown
|
||||
* to the user as well as the index of the card that should initially be
|
||||
* presented as the selected card.
|
||||
* presented as the selected card. The list should not contain more than the
|
||||
* maximum number of cards requested.
|
||||
*/
|
||||
public void onSuccess(@NonNull GetWalletCardsResponse response) {
|
||||
mHandler.post(() -> onSuccessInternal(response));
|
||||
}
|
||||
void onSuccess(@NonNull GetWalletCardsResponse response);
|
||||
|
||||
/**
|
||||
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
|
||||
@@ -60,33 +42,5 @@ public final class GetWalletCardsCallback {
|
||||
* (Personally Identifiable Information, such as username or email address).
|
||||
* @throws IllegalStateException if this method or {@link #onSuccess} was already called.
|
||||
*/
|
||||
public void onFailure(@NonNull GetWalletCardsError error) {
|
||||
mHandler.post(() -> onFailureInternal(error));
|
||||
}
|
||||
|
||||
private void onSuccessInternal(GetWalletCardsResponse response) {
|
||||
if (mCalled) {
|
||||
Log.w(TAG, "already called");
|
||||
return;
|
||||
}
|
||||
mCalled = true;
|
||||
try {
|
||||
mCallback.onGetWalletCardsSuccess(response);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error returning wallet cards", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onFailureInternal(GetWalletCardsError error) {
|
||||
if (mCalled) {
|
||||
Log.w(TAG, "already called");
|
||||
return;
|
||||
}
|
||||
mCalled = true;
|
||||
try {
|
||||
mCallback.onGetWalletCardsFailure(error);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error returning failure message", e);
|
||||
}
|
||||
}
|
||||
void onFailure(@NonNull GetWalletCardsError error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.quickaccesswallet;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
|
||||
|
||||
private static final String TAG = "QAWalletCallback";
|
||||
|
||||
private final IQuickAccessWalletServiceCallbacks mCallback;
|
||||
private final GetWalletCardsRequest mRequest;
|
||||
private final Handler mHandler;
|
||||
private boolean mCalled;
|
||||
|
||||
GetWalletCardsCallbackImpl(GetWalletCardsRequest request,
|
||||
IQuickAccessWalletServiceCallbacks callback, Handler handler) {
|
||||
mRequest = request;
|
||||
mCallback = callback;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
|
||||
* was successfully handled by the service.
|
||||
*
|
||||
* @param response The response contains the list of {@link WalletCard walletCards} to be shown
|
||||
* to the user as well as the index of the card that should initially be
|
||||
* presented as the selected card.
|
||||
*/
|
||||
public void onSuccess(@NonNull GetWalletCardsResponse response) {
|
||||
Log.i(TAG, "onSuccess");
|
||||
if (isValidResponse(response)) {
|
||||
mHandler.post(() -> onSuccessInternal(response));
|
||||
} else {
|
||||
Log.w(TAG, "Invalid GetWalletCards response");
|
||||
mHandler.post(() -> onFailureInternal(new GetWalletCardsError(null, null)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
|
||||
* could not be handled by the service.
|
||||
*
|
||||
* @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII
|
||||
* (Personally Identifiable Information, such as username or email address).
|
||||
* @throws IllegalStateException if this method or {@link #onSuccess} was already called.
|
||||
*/
|
||||
public void onFailure(@NonNull GetWalletCardsError error) {
|
||||
mHandler.post(() -> onFailureInternal(error));
|
||||
}
|
||||
|
||||
private void onSuccessInternal(GetWalletCardsResponse response) {
|
||||
Log.i(TAG, "onSuccessInternal");
|
||||
if (mCalled) {
|
||||
Log.w(TAG, "already called");
|
||||
return;
|
||||
}
|
||||
mCalled = true;
|
||||
try {
|
||||
mCallback.onGetWalletCardsSuccess(response);
|
||||
Log.i(TAG, "onSuccessInternal: returned response");
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Error returning wallet cards", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onFailureInternal(GetWalletCardsError error) {
|
||||
if (mCalled) {
|
||||
Log.w(TAG, "already called");
|
||||
return;
|
||||
}
|
||||
mCalled = true;
|
||||
try {
|
||||
mCallback.onGetWalletCardsFailure(error);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error returning failure message", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
|
||||
return response != null
|
||||
&& response.getWalletCards() != null
|
||||
&& response.getSelectedIndex() >= 0
|
||||
&& (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
|
||||
|| response.getSelectedIndex() < response.getWalletCards().size())
|
||||
&& response.getWalletCards().size() < mRequest.getMaxCards()
|
||||
&& areValidCards(response.getWalletCards());
|
||||
}
|
||||
|
||||
private boolean areValidCards(List<WalletCard> walletCards) {
|
||||
for (WalletCard walletCard : walletCards) {
|
||||
if (walletCard == null
|
||||
|| walletCard.getCardId() == null
|
||||
|| walletCard.getCardImage() == null
|
||||
|| TextUtils.isEmpty(walletCard.getContentDescription())
|
||||
|| walletCard.getPendingIntent() == null) {
|
||||
return false;
|
||||
}
|
||||
Icon cardImage = walletCard.getCardImage();
|
||||
if (cardImage.getType() == Icon.TYPE_BITMAP
|
||||
&& walletCard.getCardImage().getBitmap().getConfig()
|
||||
!= Bitmap.Config.HARDWARE) {
|
||||
Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,13 @@ public final class GetWalletCardsResponse implements Parcelable {
|
||||
/**
|
||||
* Construct a new response.
|
||||
*
|
||||
* @param walletCards The list of wallet cards.
|
||||
* @param walletCards The list of wallet cards. The list may be empty but must NOT be larger
|
||||
* than {@link GetWalletCardsRequest#getMaxCards()}. The list may not
|
||||
* contain null values.
|
||||
* @param selectedIndex The index of the card that should be presented as the initially
|
||||
* 'selected' card
|
||||
* 'selected' card. The index must be greater than or equal to zero and
|
||||
* less than the size of the list of walletCards (unless the list is empty
|
||||
* in which case the value may be 0).
|
||||
*/
|
||||
public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) {
|
||||
this.mWalletCards = walletCards;
|
||||
|
||||
@@ -18,16 +18,16 @@ package android.service.quickaccesswallet;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Facilitates accessing cards from the {@link QuickAccessWalletService}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public interface QuickAccessWalletClient {
|
||||
|
||||
/**
|
||||
@@ -40,17 +40,65 @@ public interface QuickAccessWalletClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the {@link QuickAccessWalletService} is available.
|
||||
* @return true if the {@link QuickAccessWalletService} is available. This means that the
|
||||
* default NFC payment application has an exported service that can provide cards to the Quick
|
||||
* Access Wallet. However, it does not mean that (1) the call will necessarily be successful,
|
||||
* nor does it mean that cards may be displayed at this time. Addition checks are required:
|
||||
* <ul>
|
||||
* <li>If {@link #isWalletFeatureAvailable()} is false, cards should not be displayed
|
||||
* <li>If the device is locked and {@link #isWalletFeatureAvailableWhenDeviceLocked} is
|
||||
* false, cards should not be displayed while the device remains locked. (A message
|
||||
* prompting the user to unlock to view cards may be appropriate).</li>
|
||||
* </ul>
|
||||
*/
|
||||
boolean isWalletServiceAvailable();
|
||||
|
||||
/**
|
||||
* Wallet cards should not be displayed if:
|
||||
* <ul>
|
||||
* <li>The wallet service is unavailable</li>
|
||||
* <li>The device is not provisioned, ie user setup is incomplete</li>
|
||||
* <li>If the wallet feature has been disabled by the user</li>
|
||||
* <li>If the phone has been put into lockdown mode</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Quick Access Wallet implementers should call this method before calling
|
||||
* {@link #getWalletCards} to ensure that cards may be displayed.
|
||||
*/
|
||||
boolean isWalletFeatureAvailable();
|
||||
|
||||
/**
|
||||
* Wallet cards may not be displayed on the lock screen if the user has opted to hide
|
||||
* notifications or sensitive content on the lock screen.
|
||||
* <ul>
|
||||
* <li>The device is not provisioned, ie user setup is incomplete</li>
|
||||
* <li>If the wallet feature has been disabled by the user</li>
|
||||
* <li>If the phone has been put into lockdown mode</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Quick Access Wallet implementers should call this method before calling
|
||||
* {@link #getWalletCards} if the device is currently locked.
|
||||
*
|
||||
* @return true if cards may be displayed on the lock screen.
|
||||
*/
|
||||
boolean isWalletFeatureAvailableWhenDeviceLocked();
|
||||
|
||||
/**
|
||||
* Get wallet cards from the {@link QuickAccessWalletService}.
|
||||
*/
|
||||
void getWalletCards(
|
||||
@NonNull GetWalletCardsRequest request,
|
||||
@NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
|
||||
@NonNull Consumer<GetWalletCardsError> onFailureListener);
|
||||
@NonNull OnWalletCardsRetrievedCallback callback);
|
||||
|
||||
/**
|
||||
* Callback for getWalletCards
|
||||
*/
|
||||
interface OnWalletCardsRetrievedCallback {
|
||||
void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response);
|
||||
|
||||
void onWalletCardRetrievalError(@NonNull GetWalletCardsError error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the {@link QuickAccessWalletService} service that a wallet card was selected.
|
||||
@@ -65,12 +113,24 @@ public interface QuickAccessWalletClient {
|
||||
/**
|
||||
* Unregister event listener.
|
||||
*/
|
||||
void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
|
||||
void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
|
||||
|
||||
/**
|
||||
* Unregister event listener
|
||||
*/
|
||||
void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
|
||||
void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
|
||||
|
||||
/**
|
||||
* A listener for {@link WalletServiceEvent walletServiceEvents}
|
||||
*/
|
||||
interface WalletServiceEventListener {
|
||||
void onWalletServiceEvent(@NonNull WalletServiceEvent event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all event listeners and disconnect from the service.
|
||||
*/
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* The manifest entry for the QuickAccessWalletService may also publish information about the
|
||||
@@ -78,12 +138,12 @@ public interface QuickAccessWalletClient {
|
||||
* application.
|
||||
*/
|
||||
@Nullable
|
||||
Intent getWalletActivity();
|
||||
Intent createWalletIntent();
|
||||
|
||||
/**
|
||||
* The manifest entry for the {@link QuickAccessWalletService} may publish the activity that
|
||||
* hosts the settings
|
||||
*/
|
||||
@Nullable
|
||||
Intent getSettingsActivity();
|
||||
Intent createWalletSettingsIntent();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -27,79 +28,225 @@ import android.content.ServiceConnection;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
|
||||
* responses, and disconnects automatically after a short period of time. The client may
|
||||
* @hide
|
||||
*/
|
||||
@SuppressWarnings("AndroidJdkLibsChecker")
|
||||
class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback,
|
||||
ServiceConnection {
|
||||
public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
|
||||
|
||||
private static final String TAG = "QAWalletSClient";
|
||||
private final Handler mHandler;
|
||||
private final Context mContext;
|
||||
private final Queue<ApiCaller> mRequestQueue;
|
||||
private final Map<Consumer<WalletServiceEvent>, String> mEventListeners;
|
||||
private final Map<WalletServiceEventListener, String> mEventListeners;
|
||||
private boolean mIsConnected;
|
||||
/**
|
||||
* Timeout for active service connections (1 minute)
|
||||
*/
|
||||
private static final long SERVICE_CONNECTION_TIMEOUT_MS = 60 * 1000;
|
||||
@Nullable
|
||||
private IQuickAccessWalletService mService;
|
||||
|
||||
|
||||
@Nullable
|
||||
private final QuickAccessWalletServiceInfo mServiceInfo;
|
||||
|
||||
private static final int MSG_CONNECT = 1;
|
||||
private static final int MSG_CONNECTED = 2;
|
||||
private static final int MSG_EXECUTE = 3;
|
||||
private static final int MSG_DISCONNECT = 4;
|
||||
private static final int MSG_TIMEOUT_SERVICE = 5;
|
||||
|
||||
QuickAccessWalletClientImpl(@NonNull Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
|
||||
mHandler = new Handler(Looper.getMainLooper(), this);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mRequestQueue = new LinkedList<>();
|
||||
mEventListeners = new HashMap<>(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_CONNECT:
|
||||
connectInternal();
|
||||
break;
|
||||
case MSG_CONNECTED:
|
||||
onConnectedInternal((IQuickAccessWalletService) msg.obj);
|
||||
break;
|
||||
case MSG_EXECUTE:
|
||||
executeInternal((ApiCaller) msg.obj);
|
||||
break;
|
||||
case MSG_DISCONNECT:
|
||||
disconnectInternal();
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Unknown what: " + msg.what);
|
||||
return false;
|
||||
public boolean isWalletServiceAvailable() {
|
||||
boolean available = mServiceInfo != null;
|
||||
Log.i(TAG, "isWalletServiceAvailable: " + available);
|
||||
return available;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWalletFeatureAvailable() {
|
||||
int currentUser = ActivityManager.getCurrentUser();
|
||||
return checkUserSetupComplete()
|
||||
&& checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
|
||||
&& !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWalletFeatureAvailableWhenDeviceLocked() {
|
||||
return checkSecureSetting(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS)
|
||||
&& checkSecureSetting(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getWalletCards(
|
||||
@NonNull GetWalletCardsRequest request,
|
||||
@NonNull OnWalletCardsRetrievedCallback callback) {
|
||||
|
||||
Log.i(TAG, "getWalletCards");
|
||||
|
||||
if (!isWalletServiceAvailable()) {
|
||||
callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
|
||||
BaseCallbacks serviceCallback = new BaseCallbacks() {
|
||||
@Override
|
||||
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
|
||||
mHandler.post(() -> callback.onWalletCardsRetrieved(response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetWalletCardsFailure(GetWalletCardsError error) {
|
||||
mHandler.post(() -> callback.onWalletCardRetrievalError(error));
|
||||
}
|
||||
};
|
||||
|
||||
executeApiCall(new ApiCaller("onWalletCardsRequested") {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletCardsRequested(request, serviceCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApiError() {
|
||||
serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
|
||||
Log.i(TAG, "selectWalletCard");
|
||||
if (!isWalletServiceAvailable()) {
|
||||
return;
|
||||
}
|
||||
executeApiCall(new ApiCaller("onWalletCardSelected") {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletCardSelected(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyWalletDismissed() {
|
||||
if (!isWalletServiceAvailable()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "notifyWalletDismissed");
|
||||
executeApiCall(new ApiCaller("onWalletDismissed") {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletDismissed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWalletServiceEventListener(WalletServiceEventListener listener) {
|
||||
if (!isWalletServiceAvailable()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "registerWalletServiceEventListener");
|
||||
BaseCallbacks callback = new BaseCallbacks() {
|
||||
@Override
|
||||
public void onWalletServiceEvent(WalletServiceEvent event) {
|
||||
Log.i(TAG, "onWalletServiceEvent");
|
||||
mHandler.post(() -> listener.onWalletServiceEvent(event));
|
||||
}
|
||||
};
|
||||
|
||||
executeApiCall(new ApiCaller("registerListener") {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
String listenerId = UUID.randomUUID().toString();
|
||||
WalletServiceEventListenerRequest request =
|
||||
new WalletServiceEventListenerRequest(listenerId);
|
||||
mEventListeners.put(listener, listenerId);
|
||||
service.registerWalletServiceEventListener(request, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWalletServiceEventListener(WalletServiceEventListener listener) {
|
||||
if (!isWalletServiceAvailable()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "unregisterWalletServiceEventListener");
|
||||
executeApiCall(new ApiCaller("unregisterListener") {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
String listenerId = mEventListeners.remove(listener);
|
||||
if (listenerId == null) {
|
||||
return;
|
||||
}
|
||||
WalletServiceEventListenerRequest request =
|
||||
new WalletServiceEventListenerRequest(listenerId);
|
||||
service.unregisterWalletServiceEventListener(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
Log.i(TAG, "disconnect");
|
||||
mHandler.post(() -> disconnectInternal(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Intent createWalletIntent() {
|
||||
if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
|
||||
return null;
|
||||
}
|
||||
return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
|
||||
.setComponent(
|
||||
new ComponentName(
|
||||
mServiceInfo.getComponentName().getPackageName(),
|
||||
mServiceInfo.getWalletActivity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Intent createWalletSettingsIntent() {
|
||||
if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
|
||||
return null;
|
||||
}
|
||||
return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
|
||||
.setComponent(
|
||||
new ComponentName(
|
||||
mServiceInfo.getComponentName().getPackageName(),
|
||||
mServiceInfo.getSettingsActivity()));
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
|
||||
Log.i(TAG, "connect");
|
||||
mHandler.post(this::connectInternal);
|
||||
}
|
||||
|
||||
private void connectInternal() {
|
||||
Log.i(TAG, "connectInternal");
|
||||
if (mServiceInfo == null) {
|
||||
Log.w(TAG, "Wallet service unavailable");
|
||||
return;
|
||||
@@ -113,15 +260,18 @@ class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Ca
|
||||
intent.setComponent(mServiceInfo.getComponentName());
|
||||
int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
|
||||
mContext.bindService(intent, this, flags);
|
||||
resetServiceConnectionTimeout();
|
||||
}
|
||||
|
||||
private void onConnectedInternal(IQuickAccessWalletService service) {
|
||||
Log.i(TAG, "onConnectedInternal");
|
||||
if (!mIsConnected) {
|
||||
Log.w(TAG, "onConnectInternal but connection closed");
|
||||
mService = null;
|
||||
return;
|
||||
}
|
||||
mService = service;
|
||||
Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
|
||||
for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
|
||||
try {
|
||||
apiCaller.performApiCall(mService);
|
||||
@@ -135,15 +285,33 @@ class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Ca
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
|
||||
/**
|
||||
* Resets the idle timeout for this connection by removing any pending timeout messages and
|
||||
* posting a new delayed message.
|
||||
*/
|
||||
private void resetServiceConnectionTimeout() {
|
||||
Log.i(TAG, "resetServiceConnectionTimeout");
|
||||
mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
|
||||
mHandler.postDelayed(
|
||||
() -> disconnectInternal(true),
|
||||
MSG_TIMEOUT_SERVICE,
|
||||
SERVICE_CONNECTION_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private void disconnectInternal() {
|
||||
private void disconnectInternal(boolean clearEventListeners) {
|
||||
Log.i(TAG, "disconnectInternal: " + clearEventListeners);
|
||||
if (!mIsConnected) {
|
||||
Log.w(TAG, "already disconnected");
|
||||
return;
|
||||
}
|
||||
if (clearEventListeners && !mEventListeners.isEmpty()) {
|
||||
Log.i(TAG, "disconnectInternal: clear event listeners");
|
||||
for (WalletServiceEventListener listener : mEventListeners.keySet()) {
|
||||
removeWalletServiceEventListener(listener);
|
||||
}
|
||||
mHandler.post(() -> disconnectInternal(false));
|
||||
return;
|
||||
}
|
||||
mIsConnected = false;
|
||||
mContext.unbindService(/*conn=*/this);
|
||||
mService = null;
|
||||
@@ -151,181 +319,81 @@ class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Ca
|
||||
mRequestQueue.clear();
|
||||
}
|
||||
|
||||
private void execute(ApiCaller apiCaller) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller));
|
||||
private void executeApiCall(ApiCaller apiCaller) {
|
||||
Log.i(TAG, "execute: " + apiCaller.mDesc);
|
||||
mHandler.post(() -> executeInternal(apiCaller));
|
||||
}
|
||||
|
||||
private void executeInternal(ApiCaller apiCall) {
|
||||
Log.i(TAG, "executeInternal: " + apiCall.mDesc);
|
||||
if (mIsConnected && mService != null) {
|
||||
try {
|
||||
apiCall.performApiCall(mService);
|
||||
Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
|
||||
resetServiceConnectionTimeout();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "executeInternal error", e);
|
||||
Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
|
||||
apiCall.onApiError();
|
||||
disconnect();
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
|
||||
mRequestQueue.add(apiCall);
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWalletServiceAvailable() {
|
||||
return mServiceInfo != null;
|
||||
}
|
||||
|
||||
private abstract static class ApiCaller {
|
||||
private final String mDesc;
|
||||
|
||||
private ApiCaller(String desc) {
|
||||
this.mDesc = desc;
|
||||
}
|
||||
|
||||
abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
|
||||
|
||||
void onApiError() {
|
||||
Log.w(TAG, "api error");
|
||||
Log.w(TAG, "api error: " + mDesc);
|
||||
}
|
||||
}
|
||||
|
||||
public void getWalletCards(
|
||||
@NonNull GetWalletCardsRequest request,
|
||||
@NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
|
||||
@NonNull Consumer<GetWalletCardsError> onFailureListener) {
|
||||
|
||||
BaseCallbacks callback = new BaseCallbacks() {
|
||||
@Override
|
||||
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
|
||||
mHandler.post(() -> onSuccessListener.accept(response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetWalletCardsFailure(GetWalletCardsError error) {
|
||||
mHandler.post(() -> onFailureListener.accept(error));
|
||||
}
|
||||
};
|
||||
|
||||
execute(new ApiCaller() {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletCardsRequested(request, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApiError() {
|
||||
callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
|
||||
execute(new ApiCaller() {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletCardSelected(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyWalletDismissed() {
|
||||
execute(new ApiCaller() {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
service.onWalletDismissed();
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
|
||||
|
||||
BaseCallbacks callback = new BaseCallbacks() {
|
||||
@Override
|
||||
public void onWalletServiceEvent(WalletServiceEvent event) {
|
||||
Log.i(TAG, "onWalletServiceEvent");
|
||||
mHandler.post(() -> listener.accept(event));
|
||||
}
|
||||
};
|
||||
|
||||
execute(new ApiCaller() {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
String listenerId = UUID.randomUUID().toString();
|
||||
WalletServiceEventListenerRequest request =
|
||||
new WalletServiceEventListenerRequest(listenerId);
|
||||
mEventListeners.put(listener, listenerId);
|
||||
service.registerWalletServiceEventListener(request, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
|
||||
execute(new ApiCaller() {
|
||||
@Override
|
||||
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
|
||||
String listenerId = mEventListeners.get(listener);
|
||||
if (listenerId == null) {
|
||||
return;
|
||||
}
|
||||
WalletServiceEventListenerRequest request =
|
||||
new WalletServiceEventListenerRequest(listenerId);
|
||||
service.unregisterWalletServiceEventListener(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Intent getWalletActivity() {
|
||||
if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
|
||||
return null;
|
||||
}
|
||||
return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
|
||||
.setComponent(
|
||||
new ComponentName(
|
||||
mServiceInfo.getComponentName().getPackageName(),
|
||||
mServiceInfo.getWalletActivity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Intent getSettingsActivity() {
|
||||
if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
|
||||
return null;
|
||||
}
|
||||
return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
|
||||
.setComponent(
|
||||
new ComponentName(
|
||||
mServiceInfo.getComponentName().getPackageName(),
|
||||
mServiceInfo.getSettingsActivity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection to the {@link QuickAccessWalletService}
|
||||
*/
|
||||
|
||||
|
||||
@Override
|
||||
@Override // ServiceConnection
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
Log.i(TAG, "onServiceConnected: " + name);
|
||||
IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service));
|
||||
mHandler.post(() -> onConnectedInternal(service));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override // ServiceConnection
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
// Do not disconnect, as we may later be re-connected
|
||||
Log.w(TAG, "onServiceDisconnected");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override // ServiceConnection
|
||||
public void onBindingDied(ComponentName name) {
|
||||
// This is a recoverable error but the client will need to reconnect.
|
||||
Log.w(TAG, "onBindingDied");
|
||||
disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override // ServiceConnection
|
||||
public void onNullBinding(ComponentName name) {
|
||||
Log.w(TAG, "onNullBinding");
|
||||
disconnect();
|
||||
}
|
||||
|
||||
private boolean checkSecureSetting(String name) {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) == 1;
|
||||
}
|
||||
|
||||
private boolean checkUserSetupComplete() {
|
||||
return Settings.Secure.getIntForUser(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.USER_SETUP_COMPLETE, 0,
|
||||
UserHandle.USER_CURRENT) == 1;
|
||||
}
|
||||
|
||||
private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub {
|
||||
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
|
||||
throw new IllegalStateException();
|
||||
|
||||
@@ -45,8 +45,9 @@ import android.util.Log;
|
||||
* android.permission.BIND_NFC_SERVICE permission.
|
||||
* <li>The user explicitly selected the application as the default payment application in
|
||||
* the Tap & pay settings screen.
|
||||
* <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
|
||||
* permission in its manifest.
|
||||
* <li>The QuickAccessWalletService requires that the binding application hold the
|
||||
* {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} permission, which only the System
|
||||
* Service can hold.
|
||||
* <li>The user explicitly enables it using Android Settings (the
|
||||
* {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it).
|
||||
* </ol>
|
||||
@@ -73,6 +74,11 @@ import android.util.Log;
|
||||
* a new card is selected, the Android System will notify the service through
|
||||
* {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the
|
||||
* card that is now selected.
|
||||
* <li>If the user commences an NFC payment, the service may send a {@link WalletServiceEvent}
|
||||
* to the System indicating that the wallet application now needs to show the activity associated
|
||||
* with making a payment. Sending a {@link WalletServiceEvent} of type
|
||||
* {@link WalletServiceEvent#TYPE_NFC_PAYMENT_STARTED} should cause the quick access wallet UI
|
||||
* to be dismissed.
|
||||
* <li>When the wallet is dismissed, the Android System will notify the service through
|
||||
* {@link #onWalletDismissed}.
|
||||
* </ol>
|
||||
@@ -110,6 +116,7 @@ import android.util.Log;
|
||||
* 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" />;
|
||||
@@ -165,29 +172,6 @@ public abstract class QuickAccessWalletService extends Service {
|
||||
public static final String ACTION_VIEW_WALLET_SETTINGS =
|
||||
"android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet.
|
||||
* <p>
|
||||
* The Quick Access Wallet may be shown in a system window on top of other Activities. If the
|
||||
* user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC
|
||||
* terminal, the wallet application will need to show a payment Activity. But if the Quick
|
||||
* Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the
|
||||
* wallet application can send a broadcast to the Android System with this action to request
|
||||
* that the Quick Access Wallet be dismissed.
|
||||
* <p>
|
||||
* This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
|
||||
* permission to ensure that it is only delivered to System UI. Furthermore, your application
|
||||
* must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET}
|
||||
* <p>
|
||||
* <pre class="prettyprint">
|
||||
* context.sendBroadcast(
|
||||
* new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE);
|
||||
* </pre>
|
||||
*/
|
||||
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_DISMISS_WALLET =
|
||||
"android.service.quickaccesswallet.action.DISMISS_WALLET";
|
||||
|
||||
/**
|
||||
* Name under which a QuickAccessWalletService component publishes information about itself.
|
||||
* This meta-data should reference an XML resource containing a
|
||||
@@ -202,8 +186,20 @@ public abstract class QuickAccessWalletService extends Service {
|
||||
public static final String SERVICE_META_DATA = "android.quickaccesswallet";
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* The service currently only supports one listener at a time. Multiple connections that
|
||||
* register different listeners will clobber the listener. This field may only be accessed from
|
||||
* the main thread.
|
||||
*/
|
||||
@Nullable
|
||||
private String mEventListenerId;
|
||||
|
||||
/**
|
||||
* The service currently only supports one listener at a time. Multiple connections that
|
||||
* register different listeners will clobber the listener. This field may only be accessed from
|
||||
* the main thread.
|
||||
*/
|
||||
@Nullable
|
||||
private IQuickAccessWalletServiceCallbacks mEventListener;
|
||||
|
||||
@@ -240,7 +236,8 @@ public abstract class QuickAccessWalletService extends Service {
|
||||
private void onWalletCardsRequestedInternal(
|
||||
GetWalletCardsRequest request,
|
||||
IQuickAccessWalletServiceCallbacks callback) {
|
||||
onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler));
|
||||
onWalletCardsRequested(request,
|
||||
new GetWalletCardsCallbackImpl(request, callback, mHandler));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,14 +247,14 @@ public abstract class QuickAccessWalletService extends Service {
|
||||
// Binding to the QuickAccessWalletService is protected by the
|
||||
// android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in
|
||||
// R. Pre-R devices can have other side-loaded applications that claim this permission.
|
||||
// This ensures that the service is only available when properly permission protected.
|
||||
// Ensures that the service is only enabled when properly permission protected.
|
||||
Log.w(TAG, "Warning: binding on pre-R device");
|
||||
}
|
||||
if (SERVICE_INTERFACE.equals(intent.getAction())) {
|
||||
return mInterface.asBinder();
|
||||
if (!SERVICE_INTERFACE.equals(intent.getAction())) {
|
||||
Log.w(TAG, "Wrong action");
|
||||
return null;
|
||||
}
|
||||
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
|
||||
return null;
|
||||
return mInterface.asBinder();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,8 +77,9 @@ class QuickAccessWalletServiceInfo {
|
||||
}
|
||||
|
||||
if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
|
||||
Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s",
|
||||
serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
|
||||
Log.w(TAG, String.format("%s.%s does not require permission %s",
|
||||
serviceInfo.packageName, serviceInfo.name,
|
||||
Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -174,22 +174,25 @@ public final class WalletCard implements Parcelable {
|
||||
* @param cardId The card id must be non-null and unique within the list of
|
||||
* cards returned. <b>Note:
|
||||
* </b> this card ID should <b>not</b> contain PII (Personally
|
||||
* Identifiable Information, * such as username or email
|
||||
* address).
|
||||
* Identifiable Information, such as username or email address).
|
||||
* @param cardImage The visual representation of the card. If the card image Icon
|
||||
* is a bitmap, it should have a width of {@link
|
||||
* GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
|
||||
* GetWalletCardsRequest#getCardHeightPx()}. If the card image
|
||||
* does not have these dimensions, it may appear distorted when it
|
||||
* is scaled to fit these dimensions on screen.
|
||||
* is scaled to fit these dimensions on screen. Bitmaps should be
|
||||
* of type {@link android.graphics.Bitmap.Config#HARDWARE} for
|
||||
* performance reasons.
|
||||
* @param contentDescription The content description of the card image. This field is
|
||||
* required.
|
||||
* required and may not be null or empty.
|
||||
* <b>Note: </b> this message should <b>not</b> contain PII
|
||||
* (Personally Identifiable Information, such as username or email
|
||||
* address).
|
||||
* @param pendingIntent If the user performs a click on the card, this PendingIntent
|
||||
* will be sent. If the device is locked, the wallet will first
|
||||
* request device unlock before sending the pending intent.
|
||||
* request device unlock before sending the pending intent. It is
|
||||
* recommended that the pending intent be immutable (use {@link
|
||||
* PendingIntent#FLAG_IMMUTABLE}).
|
||||
*/
|
||||
public Builder(@NonNull String cardId,
|
||||
@NonNull Icon cardImage,
|
||||
|
||||
Reference in New Issue
Block a user