NFC payment settings.

First version, pending final UX.

Change-Id: I357e900c3f2012b35814ae197c49a8c9b97b7148
This commit is contained in:
Martijn Coenen
2013-08-01 18:13:33 -07:00
parent 8a181dd0d1
commit 26515da087
11 changed files with 519 additions and 1 deletions

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2013 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 com.android.settings.nfc;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter;
import android.nfc.cardemulation.ApduServiceInfo;
import android.nfc.cardemulation.CardEmulationManager;
import android.provider.Settings;
import java.util.ArrayList;
import java.util.List;
public class PaymentBackend {
public static final String TAG = "Settings.PaymentBackend";
public static class PaymentAppInfo {
CharSequence caption;
Drawable icon;
boolean isDefault;
public ComponentName componentName;
}
private final Context mContext;
private final NfcAdapter mAdapter;
private final CardEmulationManager mCardEmuManager;
public PaymentBackend(Context context) {
mContext = context;
mAdapter = NfcAdapter.getDefaultAdapter(context);
mCardEmuManager = CardEmulationManager.getInstance(mAdapter);
}
public List<PaymentAppInfo> getPaymentAppInfos() {
PackageManager pm = mContext.getPackageManager();
List<ApduServiceInfo> serviceInfos =
mCardEmuManager.getServices(CardEmulationManager.CATEGORY_PAYMENT);
List<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
if (serviceInfos == null) return appInfos;
ComponentName defaultApp = getDefaultPaymentApp();
for (ApduServiceInfo service : serviceInfos) {
PaymentAppInfo appInfo = new PaymentAppInfo();
appInfo.caption = service.loadLabel(pm);
appInfo.icon = service.loadIcon(pm);
appInfo.isDefault = service.getComponent().equals(defaultApp);
appInfo.componentName = service.getComponent();
appInfos.add(appInfo);
}
return appInfos;
}
ComponentName getDefaultPaymentApp() {
String componentString = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
if (componentString != null) {
return ComponentName.unflattenFromString(componentString);
} else {
return null;
}
}
public void setDefaultPaymentApp(ComponentName app) {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
app != null ? app.flattenToString() : null);
}
public boolean isAutoPaymentMode() {
String mode = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_MODE);
return (!CardEmulationManager.PAYMENT_MODE_MANUAL.equals(mode));
}
public void setAutoPaymentMode(boolean enable) {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_MODE,
enable ? CardEmulationManager.PAYMENT_MODE_AUTO
: CardEmulationManager.PAYMENT_MODE_MANUAL);
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2013 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 com.android.settings.nfc;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.nfc.cardemulation.CardEmulationManager;
import android.os.Bundle;
import android.util.Log;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
import java.util.List;
public final class PaymentDefaultDialog extends AlertActivity implements
DialogInterface.OnClickListener {
public static final String TAG = "PaymentDefaultDialog";
private PaymentBackend mBackend;
private ComponentName mNewDefault;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBackend = new PaymentBackend(this);
Intent intent = getIntent();
ComponentName component = intent.getParcelableExtra(
CardEmulationManager.EXTRA_SERVICE_COMPONENT);
String category = intent.getStringExtra(CardEmulationManager.EXTRA_CATEGORY);
if (!buildDialog(component, category)) {
finish();
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
mBackend.setDefaultPaymentApp(mNewDefault);
mBackend.setAutoPaymentMode(true);
break;
case BUTTON_NEGATIVE:
break;
}
}
private boolean buildDialog(ComponentName component, String category) {
if (component == null || category == null) {
Log.e(TAG, "Component or category are null");
return false;
}
if (!CardEmulationManager.CATEGORY_PAYMENT.equals(category)) {
Log.e(TAG, "Don't support defaults for category " + category);
return false;
}
// Check if passed in service exists
boolean found = false;
List<PaymentAppInfo> services = mBackend.getPaymentAppInfos();
for (PaymentAppInfo service : services) {
if (component.equals(service.componentName)) {
found = true;
break;
}
}
if (!found) {
Log.e(TAG, "Component " + component + " is not a registered payment service.");
return false;
}
// Get current mode and default component
boolean isAuto = mBackend.isAutoPaymentMode();
ComponentName defaultComponent = mBackend.getDefaultPaymentApp();
if (defaultComponent != null && defaultComponent.equals(component)) {
Log.e(TAG, "Component " + component + " is already default.");
return false;
}
PackageManager pm = getPackageManager();
ApplicationInfo newAppInfo;
try {
newAppInfo = pm.getApplicationInfo(component.getPackageName(), 0);
} catch (NameNotFoundException e) {
Log.e(TAG, "PM could not load app info for " + component);
return false;
}
ApplicationInfo defaultAppInfo = null;
try {
if (defaultComponent != null) {
defaultAppInfo = pm.getApplicationInfo(defaultComponent.getPackageName(), 0);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "PM could not load app info for " + defaultComponent);
// Continue intentionally
}
mNewDefault = component;
// Compose dialog; get
final AlertController.AlertParams p = mAlertParams;
p.mTitle = getString(R.string.nfc_payment_set_default);
if (defaultAppInfo == null || !isAuto) {
p.mMessage = "Always use " + newAppInfo.loadLabel(pm) + " when you tap and pay?";
} else {
p.mMessage = "Always use " + newAppInfo.loadLabel(pm) + " instead of " +
defaultAppInfo.loadLabel(pm) + " when you tap and pay?";
}
p.mPositiveButtonText = getString(R.string.yes);
p.mNegativeButtonText = getString(R.string.no);
p.mPositiveButtonListener = this;
p.mNegativeButtonListener = this;
setupAlert();
return true;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2013 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 com.android.settings.nfc;
import android.content.Context;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
import java.util.List;
public class PaymentSettings extends SettingsPreferenceFragment implements
OnClickListener {
public static final String TAG = "PaymentSettings";
private PaymentBackend mPaymentBackend;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setHasOptionsMenu(false);
mPaymentBackend = new PaymentBackend(getActivity());
}
public void refresh() {
PreferenceManager manager = getPreferenceManager();
PreferenceScreen screen = manager.createPreferenceScreen(getActivity());
boolean isAuto = mPaymentBackend.isAutoPaymentMode();
// Get all payment services
List<PaymentAppInfo> appInfos = mPaymentBackend.getPaymentAppInfos();
if (appInfos != null && appInfos.size() > 0) {
// Add all payment apps
for (PaymentAppInfo appInfo : appInfos) {
PaymentAppPreference preference =
new PaymentAppPreference(getActivity(), appInfo, this);
// If for some reason isAuto gets out of sync, clear out app default
appInfo.isDefault &= isAuto;
preference.setIcon(appInfo.icon);
preference.setTitle(appInfo.caption);
screen.addPreference(preference);
}
if (appInfos.size() > 1) {
PaymentAppInfo appInfo = new PaymentAppInfo();
appInfo.icon = null;
appInfo.componentName = null;
appInfo.isDefault = !isAuto;
// Add "Ask every time" option
PaymentAppPreference preference =
new PaymentAppPreference(getActivity(), appInfo, this);
preference.setIcon(null);
preference.setTitle(R.string.nfc_payment_ask);
screen.addPreference(preference);
}
}
setPreferenceScreen(screen);
}
@Override
public void onClick(View v) {
if (v.getTag() instanceof PaymentAppInfo) {
PaymentAppInfo appInfo = (PaymentAppInfo) v.getTag();
if (appInfo.componentName != null) {
mPaymentBackend.setDefaultPaymentApp(appInfo.componentName);
mPaymentBackend.setAutoPaymentMode(true);
} else {
mPaymentBackend.setDefaultPaymentApp(null);
mPaymentBackend.setAutoPaymentMode(false);
}
refresh();
}
}
@Override
public void onResume() {
super.onResume();
refresh();
}
public static class PaymentAppPreference extends Preference {
private final OnClickListener listener;
private final PaymentAppInfo appInfo;
public PaymentAppPreference(Context context, PaymentAppInfo appInfo,
OnClickListener listener) {
super(context);
setLayoutResource(R.layout.nfc_payment_option);
this.appInfo = appInfo;
this.listener = listener;
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
view.setOnClickListener(listener);
view.setTag(appInfo);
RadioButton radioButton = (RadioButton) view.findViewById(android.R.id.button1);
radioButton.setChecked(appInfo.isDefault);
}
}
}