summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml25
-rw-r--r--proguard.flags1
-rw-r--r--res/drawable-xhdpi/ic_settings_nfc_payment.pngbin0 -> 7154 bytes
-rw-r--r--res/layout/nfc_payment_option.xml80
-rw-r--r--res/values/strings.xml9
-rw-r--r--res/xml/nfc_payment_settings.xml18
-rw-r--r--res/xml/settings_headers.xml7
-rw-r--r--src/com/android/settings/Settings.java8
-rw-r--r--src/com/android/settings/nfc/PaymentBackend.java102
-rw-r--r--src/com/android/settings/nfc/PaymentDefaultDialog.java144
-rw-r--r--src/com/android/settings/nfc/PaymentSettings.java126
11 files changed, 519 insertions, 1 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 10c66d4..5769305 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1571,6 +1571,31 @@
android:resource="@id/user_settings" />
</activity>
+ <activity android:name="Settings$PaymentSettingsActivity"
+ android:uiOptions="splitActionBarWhenNarrow"
+ android:label="@string/nfc_payment_settings_title"
+ android:taskAffinity=""
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.settings.NFC_PAYMENT_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.nfc.PaymentSettings" />
+ <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+ android:resource="@id/nfc_payment_settings" />
+ </activity>
+ <activity android:name=".nfc.PaymentDefaultDialog"
+ android:label="@string/nfc_payment_set_default"
+ android:excludeFromRecents="true"
+ android:theme="@*android:style/Theme.Holo.Light.Dialog.Alert">
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.ACTION_CHANGE_DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="Settings$NotificationAccessSettingsActivity"
android:label="@string/manage_notification_access"
android:taskAffinity=""
diff --git a/proguard.flags b/proguard.flags
index 8a6c2cb..211a5a4 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -13,6 +13,7 @@
-keep class com.android.settings.fuelgauge.*
-keep class com.android.settings.users.*
-keep class com.android.settings.NotificationStation
+-keep class com.android.settings.nfc.*
# Keep click responders
-keepclassmembers class com.android.settings.inputmethod.UserDictionaryAddWordActivity {
diff --git a/res/drawable-xhdpi/ic_settings_nfc_payment.png b/res/drawable-xhdpi/ic_settings_nfc_payment.png
new file mode 100644
index 0000000..3590a15
--- /dev/null
+++ b/res/drawable-xhdpi/ic_settings_nfc_payment.png
Binary files differ
diff --git a/res/layout/nfc_payment_option.xml b/res/layout/nfc_payment_option.xml
new file mode 100644
index 0000000..122e041
--- /dev/null
+++ b/res/layout/nfc_payment_option.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:id="@+id/nfc_payment_pref"
+ android:focusable="true"
+ android:clickable="true"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:background="?android:attr/selectableItemBackground">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:minWidth="@*android:dimen/preference_icon_minWidth"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_gravity="center"
+ android:minWidth="48dp"
+ android:scaleType="centerInside"
+ android:layout_marginEnd="@*android:dimen/preference_item_padding_inner"
+ />
+ </LinearLayout>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="6dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1">
+ <TextView
+ android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:paddingBottom="3dip"
+ android:visibility="gone"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="13sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:focusable="false"
+ android:maxLines="4" />
+ </RelativeLayout>
+ <RadioButton
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:duplicateParentState="true"
+ android:clickable="false"
+ android:focusable="false" />
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a4a3aec..9d20fab 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4589,6 +4589,15 @@
<!-- Warning message title for global font change [CHAR LIMIT=40] -->
<string name="global_font_change_title">Change font size</string>
+ <!-- NFC payment settings --><skip/>
+ <string name="nfc_payment_settings_title">Tap and Pay</string>
+ <!-- Option to tell Android to ask the user which payment app to use every time
+ a payment terminal is tapped -->
+ <string name="nfc_payment_ask">Ask every time</string>
+ <!-- Label for the dialog that is shown when the user is asked to set a
+ preferred payment application -->
+ <string name="nfc_payment_set_default">Set as your preference?</string>
+
<!-- Restrictions settings --><skip/>
<!-- Restriction settings title [CHAR LIMIT=35] -->
diff --git a/res/xml/nfc_payment_settings.xml b/res/xml/nfc_payment_settings.xml
new file mode 100644
index 0000000..9b47dda
--- /dev/null
+++ b/res/xml/nfc_payment_settings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+</PreferenceScreen>
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index 867fc19..65c42ef 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -104,6 +104,13 @@
android:title="@string/user_settings_title"
android:id="@+id/user_settings" />
+ <!-- Manage NFC payment apps -->
+ <header
+ android:fragment="com.android.settings.nfc.PaymentSettings"
+ android:icon="@drawable/ic_settings_nfc_payment"
+ android:title="@string/nfc_payment_settings_title"
+ android:id="@+id/nfc_payment_settings" />
+
<!-- Manufacturer hook -->
<header
android:fragment="com.android.settings.WirelessSettings"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 4221059..c327f4a 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -143,7 +143,8 @@ public class Settings extends PreferenceActivity
R.id.date_time_settings,
R.id.about_settings,
R.id.accessibility_settings,
- R.id.print_settings
+ R.id.print_settings,
+ R.id.nfc_payment_settings
};
private SharedPreferences mDevelopmentPreferences;
@@ -551,6 +552,10 @@ public class Settings extends PreferenceActivity
|| Utils.isMonkeyRunning()) {
target.remove(i);
}
+ } else if (id == R.id.nfc_payment_settings) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
+ target.remove(i);
+ }
} else if (id == R.id.development_settings) {
if (!showDev) {
target.remove(i);
@@ -945,4 +950,5 @@ public class Settings extends PreferenceActivity
public static class UserSettingsActivity extends Settings { /* empty */ }
public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
public static class UsbSettingsActivity extends Settings { /* empty */ }
+ public static class NfcPaymentActivity extends Settings { /* empty */ }
}
diff --git a/src/com/android/settings/nfc/PaymentBackend.java b/src/com/android/settings/nfc/PaymentBackend.java
new file mode 100644
index 0000000..fc0f4a3
--- /dev/null
+++ b/src/com/android/settings/nfc/PaymentBackend.java
@@ -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);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java
new file mode 100644
index 0000000..f3217dd
--- /dev/null
+++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java
@@ -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;
+ }
+
+}
diff --git a/src/com/android/settings/nfc/PaymentSettings.java b/src/com/android/settings/nfc/PaymentSettings.java
new file mode 100644
index 0000000..a1ed883
--- /dev/null
+++ b/src/com/android/settings/nfc/PaymentSettings.java
@@ -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);
+ }
+ }
+} \ No newline at end of file