diff options
author | Martijn Coenen <maco@google.com> | 2015-04-21 13:47:55 +0200 |
---|---|---|
committer | Martijn Coenen <maco@google.com> | 2015-04-28 08:17:22 +0000 |
commit | fe58b534f6a1bf63dadb18dae13c59ed8a014eec (patch) | |
tree | 12d3512f9cbccdfc42a853249b71a7dda1675d0f | |
parent | 79670bbe68dbc6c6cc1da9f1d093dabb8099e7f5 (diff) | |
download | packages_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.png | bin | 0 -> 8462 bytes | |||
-rw-r--r-- | res/drawable-xhdpi/tapandpay_emptystate.png | bin | 0 -> 20791 bytes | |||
-rw-r--r-- | res/drawable-xxhdpi/tapandpay_emptystate.png | bin | 0 -> 34918 bytes | |||
-rw-r--r-- | res/drawable-xxxhdpi/tapandpay_emptystate.png | bin | 0 -> 51173 bytes | |||
-rw-r--r-- | res/layout/nfc_payment.xml | 101 | ||||
-rw-r--r-- | res/layout/nfc_payment_empty.xml | 37 | ||||
-rw-r--r-- | res/layout/nfc_payment_option.xml | 40 | ||||
-rw-r--r-- | res/layout/preference_widget_settings.xml | 33 | ||||
-rw-r--r-- | res/values/strings.xml | 35 | ||||
-rw-r--r-- | src/com/android/settings/nfc/NfcForegroundPreference.java | 68 | ||||
-rw-r--r-- | src/com/android/settings/nfc/NfcPaymentPreference.java | 211 | ||||
-rw-r--r-- | src/com/android/settings/nfc/PaymentBackend.java | 184 | ||||
-rw-r--r-- | src/com/android/settings/nfc/PaymentDefaultDialog.java | 6 | ||||
-rw-r--r-- | src/com/android/settings/nfc/PaymentSettings.java | 193 |
14 files changed, 638 insertions, 270 deletions
diff --git a/res/drawable-mdpi/tapandpay_emptystate.png b/res/drawable-mdpi/tapandpay_emptystate.png Binary files differnew file mode 100644 index 0000000..1c0eb76 --- /dev/null +++ b/res/drawable-mdpi/tapandpay_emptystate.png diff --git a/res/drawable-xhdpi/tapandpay_emptystate.png b/res/drawable-xhdpi/tapandpay_emptystate.png Binary files differnew file mode 100644 index 0000000..f7f436b --- /dev/null +++ b/res/drawable-xhdpi/tapandpay_emptystate.png diff --git a/res/drawable-xxhdpi/tapandpay_emptystate.png b/res/drawable-xxhdpi/tapandpay_emptystate.png Binary files differnew file mode 100644 index 0000000..6e60022 --- /dev/null +++ b/res/drawable-xxhdpi/tapandpay_emptystate.png diff --git a/res/drawable-xxxhdpi/tapandpay_emptystate.png b/res/drawable-xxxhdpi/tapandpay_emptystate.png Binary files differnew file mode 100644 index 0000000..de8e437 --- /dev/null +++ b/res/drawable-xxxhdpi/tapandpay_emptystate.png 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 & 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 & 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 & 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 & 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 & 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 & pay?</string> + <string name="nfc_payment_set_default">Always use <xliff:g id="app">%1$s</xliff:g> when you Tap & 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 & 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 } } |