Merge "New Tap & Pay UX." into mnc-dev
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"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="5dp" />
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
 
-</FrameLayout>
+     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:orientation="vertical">
+
+    <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..6d9ac2fe
--- /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 cde0f24..6827867 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5517,24 +5517,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 @@
 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>();
+        ArrayList<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
 
-        if (serviceInfos == null) return appInfos;
-
-        ComponentName defaultApp = getDefaultPaymentApp();
-
-        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.isDefault = service.getComponent().equals(defaultApp);
-            appInfo.componentName = service.getComponent();
-            appInfos.add(appInfo);
+        if (serviceInfos == null) {
+            makeCallbacks();
+            return;
         }
 
-        return appInfos;
+        ComponentName defaultAppName = getDefaultPaymentApp();
+        PaymentAppInfo foundDefaultApp = null;
+        for (ApduServiceInfo service : serviceInfos) {
+            PaymentAppInfo appInfo = new PaymentAppInfo();
+            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.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;
+        }
+    }
+
+    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 @@
         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 @@
         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 @@
         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
     }
 }