summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartijn Coenen <maco@google.com>2015-04-21 13:47:55 +0200
committerMartijn Coenen <maco@google.com>2015-04-28 08:17:22 +0000
commitfe58b534f6a1bf63dadb18dae13c59ed8a014eec (patch)
tree12d3512f9cbccdfc42a853249b71a7dda1675d0f
parent79670bbe68dbc6c6cc1da9f1d093dabb8099e7f5 (diff)
downloadpackages_apps_Settings-fe58b534f6a1bf63dadb18dae13c59ed8a014eec.zip
packages_apps_Settings-fe58b534f6a1bf63dadb18dae13c59ed8a014eec.tar.gz
packages_apps_Settings-fe58b534f6a1bf63dadb18dae13c59ed8a014eec.tar.bz2
New Tap & Pay UX.
Change-Id: Icbffe0f58d2c37d5691357c13a14ab9a40e53249
-rw-r--r--res/drawable-mdpi/tapandpay_emptystate.pngbin0 -> 8462 bytes
-rw-r--r--res/drawable-xhdpi/tapandpay_emptystate.pngbin0 -> 20791 bytes
-rw-r--r--res/drawable-xxhdpi/tapandpay_emptystate.pngbin0 -> 34918 bytes
-rw-r--r--res/drawable-xxxhdpi/tapandpay_emptystate.pngbin0 -> 51173 bytes
-rw-r--r--res/layout/nfc_payment.xml101
-rw-r--r--res/layout/nfc_payment_empty.xml37
-rw-r--r--res/layout/nfc_payment_option.xml40
-rw-r--r--res/layout/preference_widget_settings.xml33
-rw-r--r--res/values/strings.xml35
-rw-r--r--src/com/android/settings/nfc/NfcForegroundPreference.java68
-rw-r--r--src/com/android/settings/nfc/NfcPaymentPreference.java211
-rw-r--r--src/com/android/settings/nfc/PaymentBackend.java184
-rw-r--r--src/com/android/settings/nfc/PaymentDefaultDialog.java6
-rw-r--r--src/com/android/settings/nfc/PaymentSettings.java193
14 files changed, 638 insertions, 270 deletions
diff --git a/res/drawable-mdpi/tapandpay_emptystate.png b/res/drawable-mdpi/tapandpay_emptystate.png
new file mode 100644
index 0000000..1c0eb76
--- /dev/null
+++ b/res/drawable-mdpi/tapandpay_emptystate.png
Binary files differ
diff --git a/res/drawable-xhdpi/tapandpay_emptystate.png b/res/drawable-xhdpi/tapandpay_emptystate.png
new file mode 100644
index 0000000..f7f436b
--- /dev/null
+++ b/res/drawable-xhdpi/tapandpay_emptystate.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tapandpay_emptystate.png b/res/drawable-xxhdpi/tapandpay_emptystate.png
new file mode 100644
index 0000000..6e60022
--- /dev/null
+++ b/res/drawable-xxhdpi/tapandpay_emptystate.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/tapandpay_emptystate.png b/res/drawable-xxxhdpi/tapandpay_emptystate.png
new file mode 100644
index 0000000..de8e437
--- /dev/null
+++ b/res/drawable-xxxhdpi/tapandpay_emptystate.png
Binary files differ
diff --git a/res/layout/nfc_payment.xml b/res/layout/nfc_payment.xml
index d6f9fa4..f56d2a4 100644
--- a/res/layout/nfc_payment.xml
+++ b/res/layout/nfc_payment.xml
@@ -1,45 +1,62 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:orientation="vertical" >
- <ImageView
- android:id="@+id/nfc_payment_tap_image"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:visibility="gone"
- android:src="@drawable/nfc_payment_empty_state"/>
- <TextView
- android:id="@+id/nfc_payment_empty_text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textSize="24sp"
- android:visibility="gone"
- android:paddingTop="16dp"
- android:text="@string/nfc_payment_no_apps"/>
- <TextView
- android:id="@+id/nfc_payment_learn_more"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:clickable="true"
- android:textSize="20sp"
- android:textStyle="italic"
- android:visibility="gone"
- android:textColor="@android:color/holo_blue_light"
- android:paddingTop="16dp"
- android:text="@string/nfc_payment_learn_more"/>
- </LinearLayout>
- <ListView
- android:id="@android:id/list"
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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_marginTop="5dp" />
+ android:orientation="vertical">
-</FrameLayout>
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <ImageView
+ android:id="@+id/nfc_payment_tap_image"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:visibility="gone"
+ android:src="@drawable/tapandpay_emptystate"/>
+ <TextView
+ android:id="@+id/nfc_payment_empty_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="24sp"
+ android:visibility="gone"
+ android:paddingTop="32dp"
+ android:text="@string/nfc_payment_no_apps"/>
+ </LinearLayout>
+ <ListView android:id="@android:id/list"
+ android:drawSelectorOnTop="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:scrollbarStyle="@integer/preference_scrollbar_style" />
+<!--
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:scrollbarStyle="@integer/preference_scrollbar_style" />
+-->
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/nfc_payment_empty.xml b/res/layout/nfc_payment_empty.xml
new file mode 100644
index 0000000..42e2fa9
--- /dev/null
+++ b/res/layout/nfc_payment_empty.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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_gravity="center"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/nfc_payment_tap_image"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tapandpay_emptystate"/>
+ <TextView
+ android:id="@+id/nfc_payment_empty_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingTop="32dp"
+ android:text="@string/nfc_payment_no_apps"/>
+</LinearLayout>
diff --git a/res/layout/nfc_payment_option.xml b/res/layout/nfc_payment_option.xml
index 76fea4f..90ba279 100644
--- a/res/layout/nfc_payment_option.xml
+++ b/res/layout/nfc_payment_option.xml
@@ -13,40 +13,28 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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="false"
- android:gravity="center_vertical"
- android:paddingTop="10dp"
- android:paddingBottom="10dp"
+ android:orientation="horizontal"
+ android:paddingLeft="24dip"
android:minHeight="?android:attr/listPreferredItemHeight"
android:background="?android:attr/selectableItemBackground">
- <FrameLayout
+ <RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button"
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="@+id/banner"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="96dp"
- android:scaleType="centerInside"
- android:clickable="true"
- />
- </FrameLayout>
- <RadioButton
- android:id="@android:id/button1"
+ android:layout_height="fill_parent"
+ android:checkMark="?android:attr/listChoiceIndicatorSingle"
+ />
+ <ImageView
+ android:id="@+id/banner"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:duplicateParentState="true"
+ android:layout_height="64dp"
+ android:scaleType="centerInside"
android:clickable="true"
- android:focusable="false" />
-</RelativeLayout>
+ />
+</LinearLayout>
diff --git a/res/layout/preference_widget_settings.xml b/res/layout/preference_widget_settings.xml
new file mode 100644
index 0000000..6d9ac2f
--- /dev/null
+++ b/res/layout/preference_widget_settings.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/scrollbarSize">
+
+ <!-- Settings button -->
+ <ImageView
+ android:id="@+id/settings_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="8dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/ic_sysbar_quicksettings"
+ android:contentDescription="@string/settings_button" />
+
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0b98428..c1f7dc3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5501,24 +5501,35 @@
<!-- NFC payment settings --><skip/>
<string name="nfc_payment_settings_title">Tap &amp; pay</string>
- <!-- Google Wallet label. [CHAR LIMIT=40] --><skip/>
- <string name="google_wallet">Google Wallet</string>
+ <!-- Caption for button linking to a page explaining how Tap and Pay works-->
+ <string name="nfc_payment_how_it_works">How it works</string>
<!-- String shown when there are no NFC payment applications installed -->
- <string name="nfc_payment_no_apps">Pay with just a tap</string>
- <!-- String shown before a checkbox, allowing the user to indicate that he wants foreground apps
- to be able to override the configured default app -->
- <string name="nfc_payment_favor_foreground">Favor foreground app</string>
- <!-- String shown when there are no NFC payment applications installed, clickable, pointing to
- a website to learn more-->
- <string name="nfc_payment_learn_more">Learn more</string>
+ <string name="nfc_payment_no_apps">Use Tap &amp; pay to make in-store purchases</string>
+ <!-- Header text that can be clicked on to change the default payment app -->
+ <string name="nfc_payment_default">Payment default</string>
+ <!-- Summary text that is shown when no default app is set -->
+ <string name="nfc_payment_default_not_set">Not set</string>
+ <!-- String indicating the label of the default payment app and a description of its state; eg Google Wallet - MasterCard 1234 -->
+ <string name="nfc_payment_app_and_desc"><xliff:g id="app">%1$s</xliff:g> - <xliff:g id="description">%2$s</xliff:g></string>
+ <!-- Header for action to choose when the open app supports TapPay -->
+ <string name="nfc_payment_open_app">If open app supports Tap &amp; pay</string>
+ <!-- If open app supports TapPay, use that app instead of the default -->
+ <string name="nfc_payment_favor_open">Use that app instead of <xliff:g id="app">%1$s</xliff:g></string>
+ <!-- If open app supports TapPay, use that app instead of the default (name of default app unknown) -->
+ <string name="nfc_payment_favor_open_default_unknown">Use that app instead</string>
+ <!-- If open app supports TapPay, still use the default app -->
+ <string name="nfc_payment_favor_default">Still use <xliff:g id="app">%1$s</xliff:g></string>
+ <!-- If open app supports TapPay, still use the default app (name of default app unknown) -->
+ <string name="nfc_payment_favor_default_default_unknown">Still use default</string>
+ <!-- Header for a dialog asking the user which payment app to use -->
+ <string name="nfc_payment_pay_with">At a Tap &amp; pay terminal, pay with:</string>
<!-- NFC More... title. [CHAR LIMIT=40] -->
<string name="nfc_more_title">More...</string>
- <string name="nfc_payment_menu_item_add_service">Find apps</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_label">Set as your preference?</string>
- <string name="nfc_payment_set_default">Always use <xliff:g id="app">%1$s</xliff:g> when you tap &amp; pay?</string>
- <string name="nfc_payment_set_default_instead_of">Always use <xliff:g id="app">%1$s</xliff:g> instead of <xliff:g id="app">%2$s</xliff:g> when you tap &amp; pay?</string>
+ <string name="nfc_payment_set_default">Always use <xliff:g id="app">%1$s</xliff:g> when you Tap &amp; pay?</string>
+ <string name="nfc_payment_set_default_instead_of">Always use <xliff:g id="app">%1$s</xliff:g> instead of <xliff:g id="app">%2$s</xliff:g> when you Tap &amp; pay?</string>
<!-- Restrictions settings --><skip/>
<!-- Restriction settings title [CHAR LIMIT=35] -->
diff --git a/src/com/android/settings/nfc/NfcForegroundPreference.java b/src/com/android/settings/nfc/NfcForegroundPreference.java
new file mode 100644
index 0000000..4f4398f
--- /dev/null
+++ b/src/com/android/settings/nfc/NfcForegroundPreference.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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 com.android.settings.DropDownPreference;
+import com.android.settings.R;
+
+public class NfcForegroundPreference extends DropDownPreference implements
+ DropDownPreference.Callback, PaymentBackend.Callback {
+
+ private final PaymentBackend mPaymentBackend;
+ public NfcForegroundPreference(Context context, PaymentBackend backend) {
+ super(context);
+ mPaymentBackend = backend;
+ mPaymentBackend.registerCallback(this);
+ setCallback(this);
+ refresh();
+ }
+
+ @Override
+ public void onPaymentAppsChanged() {
+ refresh();
+ }
+
+ void refresh() {
+ PaymentBackend.PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp();
+ boolean foregroundMode = mPaymentBackend.isForegroundMode();
+ setPersistent(false);
+ setTitle(getContext().getString(R.string.nfc_payment_open_app));
+ CharSequence favorOpen;
+ CharSequence favorDefault;
+ clearItems();
+ if (defaultApp == null) {
+ favorOpen = getContext().getString(R.string.nfc_payment_favor_open_default_unknown);
+ favorDefault = getContext().getString(R.string.nfc_payment_favor_default_default_unknown);
+ } else {
+ favorOpen = getContext().getString(R.string.nfc_payment_favor_open, defaultApp.label);
+ favorDefault = getContext().getString(R.string.nfc_payment_favor_default, defaultApp.label);
+ }
+ addItem(favorOpen.toString(), true);
+ addItem(favorDefault.toString(), false);
+ if (foregroundMode) {
+ setSelectedValue(true);
+ } else {
+ setSelectedValue(false);
+ }
+ }
+
+ @Override
+ public boolean onItemSelected(int pos, Object value) {
+ mPaymentBackend.setForegroundMode((Boolean) value);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/nfc/NfcPaymentPreference.java b/src/com/android/settings/nfc/NfcPaymentPreference.java
new file mode 100644
index 0000000..e24a651
--- /dev/null
+++ b/src/com/android/settings/nfc/NfcPaymentPreference.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.preference.DialogPreference;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import com.android.settings.R;
+import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
+
+import java.util.List;
+
+public class NfcPaymentPreference extends DialogPreference implements
+ DialogInterface.OnClickListener, PaymentBackend.Callback, View.OnClickListener {
+
+ private static final String TAG = "NfcPaymentPreference";
+
+ private final NfcPaymentAdapter mAdapter;
+ private final Context mContext;
+ private final LayoutInflater mLayoutInflater;
+ private final PaymentBackend mPaymentBackend;
+
+ // Fields below only modified on UI thread
+ private ImageView mSettingsButtonView;
+
+ public NfcPaymentPreference(Context context, PaymentBackend backend) {
+ super(context, null);
+ mPaymentBackend = backend;
+ mContext = context;
+ backend.registerCallback(this);
+ mAdapter = new NfcPaymentAdapter();
+ setDialogTitle(context.getString(R.string.nfc_payment_pay_with));
+ mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ setWidgetLayoutResource(R.layout.preference_widget_settings);
+
+ refresh();
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+
+ mSettingsButtonView = (ImageView) view.findViewById(R.id.settings_button);
+ mSettingsButtonView.setOnClickListener(this);
+
+ updateSettingsVisibility();
+ }
+
+ /**
+ * MUST be called on UI thread.
+ */
+ public void refresh() {
+ List<PaymentAppInfo> appInfos = mPaymentBackend.getPaymentAppInfos();
+ PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp();
+ if (appInfos != null) {
+ PaymentAppInfo[] apps = appInfos.toArray(new PaymentAppInfo[appInfos.size()]);
+ mAdapter.updateApps(apps, defaultApp);
+ }
+ setTitle(R.string.nfc_payment_default);
+ if (defaultApp != null) {
+ setSummary(mContext.getString(R.string.nfc_payment_app_and_desc,
+ defaultApp.label, defaultApp.description));
+ } else {
+ setSummary(mContext.getString(R.string.nfc_payment_default_not_set));
+ }
+ updateSettingsVisibility();
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+
+ builder.setSingleChoiceItems(mAdapter, 0, this);
+ }
+
+ @Override
+ public void onPaymentAppsChanged() {
+ refresh();
+ }
+
+ @Override
+ public void onClick(View view) {
+ PaymentAppInfo defaultAppInfo = mPaymentBackend.getDefaultApp();
+ if (defaultAppInfo != null && defaultAppInfo.settingsComponent != null) {
+ Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
+ settingsIntent.setComponent(defaultAppInfo.settingsComponent);
+ settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivity(settingsIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Settings activity not found.");
+ }
+ }
+ }
+
+ void updateSettingsVisibility() {
+ if (mSettingsButtonView != null) {
+ PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp();
+ if (defaultApp == null || defaultApp.settingsComponent == null) {
+ mSettingsButtonView.setVisibility(View.GONE);
+ } else {
+ mSettingsButtonView.setVisibility(View.VISIBLE);
+
+ }
+ }
+ }
+
+ class NfcPaymentAdapter extends BaseAdapter implements CompoundButton.OnCheckedChangeListener,
+ View.OnClickListener {
+ // Only modified on UI thread
+ private PaymentAppInfo[] appInfos;
+
+ public NfcPaymentAdapter() {
+ }
+
+ public void updateApps(PaymentAppInfo[] appInfos, PaymentAppInfo currentDefault) {
+ // Clone app infos, only add those with a banner
+ this.appInfos = appInfos;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return appInfos.length;
+ }
+
+ @Override
+ public PaymentAppInfo getItem(int i) {
+ return appInfos[i];
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return appInfos[i].componentName.hashCode();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+ PaymentAppInfo appInfo = appInfos[position];
+ if (convertView == null) {
+ convertView = mLayoutInflater.inflate(
+ R.layout.nfc_payment_option, parent, false);
+ holder = new ViewHolder();
+ holder.imageView = (ImageView) convertView.findViewById(R.id.banner);
+ holder.radioButton = (RadioButton) convertView.findViewById(R.id.button);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ holder.imageView.setImageDrawable(appInfo.banner);
+ holder.imageView.setTag(appInfo);
+ holder.imageView.setOnClickListener(this);
+
+ // Prevent checked callback getting called on recycled views
+ holder.radioButton.setOnCheckedChangeListener(null);
+ holder.radioButton.setChecked(appInfo.isDefault);
+ holder.radioButton.setOnCheckedChangeListener(this);
+ holder.radioButton.setTag(appInfo);
+ return convertView;
+ }
+
+ public class ViewHolder {
+ public ImageView imageView;
+ public RadioButton radioButton;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ PaymentAppInfo appInfo = (PaymentAppInfo) compoundButton.getTag();
+ makeDefault(appInfo);
+ }
+
+ @Override
+ public void onClick(View view) {
+ PaymentAppInfo appInfo = (PaymentAppInfo) view.getTag();
+ makeDefault(appInfo);
+ }
+
+ void makeDefault(PaymentAppInfo appInfo) {
+ if (!appInfo.isDefault) {
+ mPaymentBackend.setDefaultPaymentApp(appInfo.componentName);
+ }
+ getDialog().dismiss();
+ }
+ }
+}
diff --git a/src/com/android/settings/nfc/PaymentBackend.java b/src/com/android/settings/nfc/PaymentBackend.java
index 25572a7..52e3f7e 100644
--- a/src/com/android/settings/nfc/PaymentBackend.java
+++ b/src/com/android/settings/nfc/PaymentBackend.java
@@ -16,15 +16,22 @@
package com.android.settings.nfc;
-import android.content.ComponentName;
-import android.content.Context;
+import android.app.Activity;
+import android.content.*;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter;
import android.nfc.cardemulation.ApduServiceInfo;
import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+import com.android.internal.content.PackageMonitor;
import java.util.ArrayList;
import java.util.List;
@@ -32,47 +39,135 @@ import java.util.List;
public class PaymentBackend {
public static final String TAG = "Settings.PaymentBackend";
+ public interface Callback {
+ void onPaymentAppsChanged();
+ }
+
public static class PaymentAppInfo {
- CharSequence caption;
+ CharSequence label;
+ CharSequence description;
Drawable banner;
boolean isDefault;
public ComponentName componentName;
+ public ComponentName settingsComponent;
}
private final Context mContext;
private final NfcAdapter mAdapter;
private final CardEmulation mCardEmuManager;
+ private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
+ // Fields below only modified on UI thread
+ private ArrayList<PaymentAppInfo> mAppInfos;
+ private PaymentAppInfo mDefaultAppInfo;
+ private ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
public PaymentBackend(Context context) {
mContext = context;
mAdapter = NfcAdapter.getDefaultAdapter(context);
mCardEmuManager = CardEmulation.getInstance(mAdapter);
+ refresh();
}
- public List<PaymentAppInfo> getPaymentAppInfos() {
+ public void onPause() {
+ mSettingsPackageMonitor.unregister();
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ public void onResume() {
+ mSettingsPackageMonitor.register(mContext, mContext.getMainLooper(), false);
+ // Register broadcast receiver for dynamic resource updates
+ IntentFilter filter = new IntentFilter(CardEmulation.ACTION_REQUEST_SERVICE_RESOURCES);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ public void refresh() {
PackageManager pm = mContext.getPackageManager();
List<ApduServiceInfo> serviceInfos =
mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT);
- List<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
-
- if (serviceInfos == null) return appInfos;
+ ArrayList<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
- ComponentName defaultApp = getDefaultPaymentApp();
+ if (serviceInfos == null) {
+ makeCallbacks();
+ return;
+ }
+ ComponentName defaultAppName = getDefaultPaymentApp();
+ PaymentAppInfo foundDefaultApp = null;
for (ApduServiceInfo service : serviceInfos) {
PaymentAppInfo appInfo = new PaymentAppInfo();
- appInfo.banner = service.loadBanner(pm);
- appInfo.caption = service.getDescription();
- if (appInfo.caption == null) {
- appInfo.caption = service.loadLabel(pm);
+ appInfo.label = service.loadLabel(pm);
+ if (appInfo.label == null) {
+ appInfo.label = service.loadAppLabel(pm);
+ }
+ appInfo.isDefault = service.getComponent().equals(defaultAppName);
+ if (appInfo.isDefault) {
+ foundDefaultApp = appInfo;
}
- appInfo.isDefault = service.getComponent().equals(defaultApp);
appInfo.componentName = service.getComponent();
+ String settingsActivity = service.getSettingsActivityName();
+ if (settingsActivity != null) {
+ appInfo.settingsComponent = new ComponentName(appInfo.componentName.getPackageName(),
+ settingsActivity);
+ } else {
+ appInfo.settingsComponent = null;
+ }
+ if (service.hasDynamicResources()) {
+ appInfo.description = "";
+ appInfo.banner = null;
+ sendBroadcastForResources(appInfo);
+ } else {
+ appInfo.description = service.getDescription();
+ appInfo.banner = service.loadBanner(pm);
+ }
appInfos.add(appInfo);
}
+ mAppInfos = appInfos;
+ mDefaultAppInfo = foundDefaultApp;
+ makeCallbacks();
+ }
+
+ public void registerCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public void unregisterCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ public List<PaymentAppInfo> getPaymentAppInfos() {
+ return mAppInfos;
+ }
+
+ public PaymentAppInfo getDefaultApp() {
+ return mDefaultAppInfo;
+ }
+
+ void makeCallbacks() {
+ for (Callback callback : mCallbacks) {
+ callback.onPaymentAppsChanged();
+ }
+ }
+
+ Drawable loadDrawableForPackage(String pkgName, int drawableResId) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ Resources res = pm.getResourcesForApplication(pkgName);
+ Drawable banner = res.getDrawable(drawableResId);
+ return banner;
+ } catch (Resources.NotFoundException e) {
+ return null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
- return appInfos;
+ void sendBroadcastForResources(PaymentAppInfo appInfo) {
+ Intent broadcastIntent = new Intent(CardEmulation.ACTION_REQUEST_SERVICE_RESOURCES);
+ broadcastIntent.setPackage(appInfo.componentName.getPackageName());
+ broadcastIntent.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT, appInfo.componentName);
+ mContext.sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.CURRENT,
+ null, mReceiver, null, Activity.RESULT_OK, null, null);
}
boolean isForegroundMode() {
@@ -103,5 +198,66 @@ public class PaymentBackend {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
app != null ? app.flattenToString() : null);
+ refresh();
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle results = getResultExtras(false);
+ if (results != null) {
+ String desc = results.getString(CardEmulation.EXTRA_DESCRIPTION);
+ int resId = results.getInt(CardEmulation.EXTRA_BANNER_RES_ID, -1);
+ // Find corresponding component
+ PaymentAppInfo matchingAppInfo = null;
+ for (PaymentAppInfo appInfo : mAppInfos) {
+ if (appInfo.componentName.equals(
+ intent.getParcelableExtra(CardEmulation.EXTRA_SERVICE_COMPONENT))) {
+ matchingAppInfo = appInfo;
+ }
+ }
+ if (matchingAppInfo != null && (desc != null || resId != -1)) {
+ if (desc != null) {
+ matchingAppInfo.description = desc;
+ }
+ if (resId != -1) {
+ matchingAppInfo.banner = loadDrawableForPackage(
+ matchingAppInfo.componentName.getPackageName(), resId);
+ }
+ makeCallbacks();
+ }
+ } else {
+ Log.e(TAG, "Didn't find results extra.");
+ }
+
+ }
+ };
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void dispatchMessage(Message msg) {
+ refresh();
+ }
+ };
+
+ private class SettingsPackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ mHandler.obtainMessage().sendToTarget();
+ }
+
+ @Override
+ public void onPackageAppeared(String packageName, int reason) {
+ mHandler.obtainMessage().sendToTarget();
+ }
+
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ mHandler.obtainMessage().sendToTarget();
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ mHandler.obtainMessage().sendToTarget();
+ }
}
} \ No newline at end of file
diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java
index 33ac947..949f87d 100644
--- a/src/com/android/settings/nfc/PaymentDefaultDialog.java
+++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java
@@ -111,13 +111,13 @@ public final class PaymentDefaultDialog extends AlertActivity implements
if (defaultPaymentApp == null) {
String formatString = getString(R.string.nfc_payment_set_default);
String msg = String.format(formatString,
- sanitizePaymentAppCaption(requestedPaymentApp.caption.toString()));
+ sanitizePaymentAppCaption(requestedPaymentApp.label.toString()));
p.mMessage = msg;
} else {
String formatString = getString(R.string.nfc_payment_set_default_instead_of);
String msg = String.format(formatString,
- sanitizePaymentAppCaption(requestedPaymentApp.caption.toString()),
- sanitizePaymentAppCaption(defaultPaymentApp.caption.toString()));
+ sanitizePaymentAppCaption(requestedPaymentApp.label.toString()),
+ sanitizePaymentAppCaption(defaultPaymentApp.label.toString()));
p.mMessage = msg;
}
p.mPositiveButtonText = getString(R.string.yes);
diff --git a/src/com/android/settings/nfc/PaymentSettings.java b/src/com/android/settings/nfc/PaymentSettings.java
index 4f04d73..ca38d92 100644
--- a/src/com/android/settings/nfc/PaymentSettings.java
+++ b/src/com/android/settings/nfc/PaymentSettings.java
@@ -16,47 +16,24 @@
package com.android.settings.nfc;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.settings.HelpUtils;
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, OnPreferenceChangeListener {
+public class PaymentSettings extends SettingsPreferenceFragment {
public static final String TAG = "PaymentSettings";
- private LayoutInflater mInflater;
private PaymentBackend mPaymentBackend;
- private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
-
@Override
protected int getMetricsCategory() {
@@ -68,180 +45,50 @@ public class PaymentSettings extends SettingsPreferenceFragment implements
super.onCreate(icicle);
mPaymentBackend = new PaymentBackend(getActivity());
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
setHasOptionsMenu(true);
}
- public void refresh() {
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ ViewGroup contentRoot = (ViewGroup) getListView().getParent();
+ View emptyView = getActivity().getLayoutInflater().inflate(
+ R.layout.nfc_payment_empty, contentRoot, false);
+ contentRoot.addView(emptyView);
+ getListView().setEmptyView(emptyView);
+
PreferenceManager manager = getPreferenceManager();
PreferenceScreen screen = manager.createPreferenceScreen(getActivity());
- // 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);
- preference.setTitle(appInfo.caption);
- if (appInfo.banner != null) {
- screen.addPreference(preference);
- } else {
- // Ignore, no banner
- Log.e(TAG, "Couldn't load banner drawable of service " + appInfo.componentName);
- }
- }
- }
- TextView emptyText = (TextView) getView().findViewById(R.id.nfc_payment_empty_text);
- TextView learnMore = (TextView) getView().findViewById(R.id.nfc_payment_learn_more);
- ImageView emptyImage = (ImageView) getView().findViewById(R.id.nfc_payment_tap_image);
- if (screen.getPreferenceCount() == 0) {
- emptyText.setVisibility(View.VISIBLE);
- learnMore.setVisibility(View.VISIBLE);
- emptyImage.setVisibility(View.VISIBLE);
- getListView().setVisibility(View.GONE);
- } else {
- SwitchPreference foreground = new SwitchPreference(getActivity());
- boolean foregroundMode = mPaymentBackend.isForegroundMode();
- foreground.setPersistent(false);
- foreground.setTitle(getString(R.string.nfc_payment_favor_foreground));
- foreground.setChecked(foregroundMode);
- foreground.setOnPreferenceChangeListener(this);
+ NfcPaymentPreference preference =
+ new NfcPaymentPreference(getActivity(), mPaymentBackend);
+ screen.addPreference(preference);
+ NfcForegroundPreference foreground = new NfcForegroundPreference(getActivity(),
+ mPaymentBackend);
screen.addPreference(foreground);
- emptyText.setVisibility(View.GONE);
- learnMore.setVisibility(View.GONE);
- emptyImage.setVisibility(View.GONE);
- getListView().setVisibility(View.VISIBLE);
}
setPreferenceScreen(screen);
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
- View v = mInflater.inflate(R.layout.nfc_payment, container, false);
- TextView learnMore = (TextView) v.findViewById(R.id.nfc_payment_learn_more);
- learnMore.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- String helpUrl;
- if (!TextUtils.isEmpty(helpUrl = getResources().getString(
- R.string.help_url_nfc_payment))) {
- final Uri fullUri = HelpUtils.uriWithAddedParameters(
- PaymentSettings.this.getActivity(), Uri.parse(helpUrl));
- Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- startActivity(intent);
- } else {
- Log.e(TAG, "Help url not set.");
- }
- }
- });
- return v;
- }
-
- @Override
- public void onClick(View v) {
- if (v.getTag() instanceof PaymentAppInfo) {
- PaymentAppInfo appInfo = (PaymentAppInfo) v.getTag();
- if (appInfo.componentName != null) {
- mPaymentBackend.setDefaultPaymentApp(appInfo.componentName);
- }
- refresh();
- }
- }
-
- @Override
public void onResume() {
super.onResume();
- mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
- refresh();
+ mPaymentBackend.onResume();
}
@Override
public void onPause() {
- mSettingsPackageMonitor.unregister();
super.onPause();
+ mPaymentBackend.onPause();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- String searchUri = Settings.Secure.getString(getContentResolver(),
- Settings.Secure.PAYMENT_SERVICE_SEARCH_URI);
- if (!TextUtils.isEmpty(searchUri)) {
- MenuItem menuItem = menu.add(R.string.nfc_payment_menu_item_add_service);
- menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menuItem.setIntent(new Intent(Intent.ACTION_VIEW,Uri.parse(searchUri)));
- }
- }
-
- private final Handler mHandler = new Handler() {
- @Override
- public void dispatchMessage(Message msg) {
- refresh();
- }
- };
-
- private class SettingsPackageMonitor extends PackageMonitor {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- mHandler.obtainMessage().sendToTarget();
- }
-
- @Override
- public void onPackageAppeared(String packageName, int reason) {
- mHandler.obtainMessage().sendToTarget();
- }
-
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- mHandler.obtainMessage().sendToTarget();
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- mHandler.obtainMessage().sendToTarget();
- }
- }
-
- 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);
-
- RadioButton radioButton = (RadioButton) view.findViewById(android.R.id.button1);
- radioButton.setChecked(appInfo.isDefault);
- radioButton.setOnClickListener(listener);
- radioButton.setTag(appInfo);
-
- ImageView banner = (ImageView) view.findViewById(R.id.banner);
- banner.setImageDrawable(appInfo.banner);
- banner.setOnClickListener(listener);
- banner.setTag(appInfo);
- }
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- if (preference instanceof SwitchPreference) {
- mPaymentBackend.setForegroundMode(((Boolean) newValue).booleanValue());
- return true;
- } else {
- return false;
- }
+ MenuItem menuItem = menu.add(R.string.nfc_payment_how_it_works);
+ menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ // TODO link to tutorial screen
}
}