New UX for the NFC default payment settings

Re-design the UX.

Bug: 202367033
Test: maunal
      make RunSettingsRoboTests ROBOTEST_FILTER=NfcForegroundPreferenceControllerTest
      make RunSettingsRoboTests ROBOTEST_FILTER=PaymentSettingsTest
Change-Id: I4c05ac4a1974645c76a37900d64aa8e1a491bca1
This commit is contained in:
Jack Yu
2022-01-04 17:40:55 +08:00
parent 8662e4642e
commit 9de30f08e6
11 changed files with 540 additions and 79 deletions

View File

@@ -1596,4 +1596,15 @@
<item>2</item> <item>2</item>
<item>4</item> <item>4</item>
</string-array> </string-array>
<!-- All the preference when the open app supports TapPay -->
<string-array name="nfc_payment_favor">
<item>Always</item>
<item>Except when another payment app is open</item>
</string-array>
<!-- All the values of the preference when the open app supports TapPay -->
<string-array name="nfc_payment_favor_values">
<item>0</item>
<item>1</item>
</string-array>
</resources> </resources>

View File

@@ -7596,7 +7596,25 @@
<string name="user_copy_apps_menu_title">Install available apps</string> <string name="user_copy_apps_menu_title">Install available apps</string>
<!-- NFC payment settings --><skip/> <!-- NFC payment settings --><skip/>
<!-- Title for NFC payment settings page [CHAR LIMIT=40] -->
<string name="nfc_payment_settings_title">Contactless payments</string> <string name="nfc_payment_settings_title">Contactless payments</string>
<!-- Title for NFC default payment settings page [CHAR LIMIT=40] -->
<string name="nfc_default_payment_settings_title">Default payment app</string>
<!-- Text string explaining how Tap and Pay works [CHAR LIMIT=100] -->
<string name="nfc_default_payment_footer">To make a payment using a payment app, hold the back of your device to a payment terminal</string>
<!-- Text string linking to a page explaining how Tap and Pay works [CHAR LIMIT=40] -->
<string name="nfc_more_details">Learn more</string>
<!-- Strings shown in a dialog when a user tries to set a work app as a nfc default payment app --><skip/>
<!-- Title for the dialog [CHAR LIMIT=40] -->
<string name="nfc_default_payment_workapp_confirmation_title">Set work app as default payment app?</string>
<!-- Header text for the dialog [CHAR LIMIT=40] -->
<string name="nfc_default_payment_workapp_confirmation_message_title">To make a payment using a work app:</string>
<!-- Text string in the dialog [CHAR LIMIT=60] -->
<string name="nfc_default_payment_workapp_confirmation_message_1">work profile must be turned on.</string>
<!-- Text string in the dialog [CHAR LIMIT=60] -->
<string name="nfc_default_payment_workapp_confirmation_message_2">you\u2019ll need to enter your work profile lock if you have one.</string>
<!-- Caption for button linking to a page explaining how Tap and Pay works--> <!-- 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 name="nfc_payment_how_it_works">How it works</string>
<!-- String shown when there are no NFC payment applications installed --> <!-- String shown when there are no NFC payment applications installed -->
@@ -7608,7 +7626,9 @@
<!-- String indicating the label of the default payment app and a description of its state; eg Google Wallet - MasterCard 1234 --> <!-- 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> <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 what to do when the open app supports TapPay: use the default set app, or the open app --> <!-- Header for what to do when the open app supports TapPay: use the default set app, or the open app -->
<string name="nfc_payment_use_default">Use default</string> <string name="nfc_payment_use_default">Use default payment app</string>
<!-- Header for a dialog asking what to do when the open app supports TapPay [CHAR LIMIT=40] -->
<string name="nfc_payment_use_default_dialog">Use default payment app</string>
<!-- Always use the default app (independent of what app is open) --> <!-- Always use the default app (independent of what app is open) -->
<string name="nfc_payment_favor_default">Always</string> <string name="nfc_payment_favor_default">Always</string>
<!-- If open app supports TapPay, use that app instead of the default --> <!-- If open app supports TapPay, use that app instead of the default -->

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/nfc_default_payment_settings_title"/>

View File

@@ -19,16 +19,17 @@
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/nfc_payment_settings_title"> android:title="@string/nfc_payment_settings_title">
<com.android.settings.nfc.NfcPaymentPreference <com.android.settings.widget.GearPreference
android:key="nfc_payment" android:key="nfc_payment_app"
android:title="@string/nfc_payment_default" android:title="@string/nfc_default_payment_settings_title"
android:dialogTitle="@string/nfc_payment_pay_with" android:fragment="com.android.settings.nfc.DefaultPaymentSettings"/>
android:widgetLayout="@layout/preference_widget_gear"
settings:controller="com.android.settings.nfc.NfcPaymentPreferenceController"/>
<DropDownPreference <ListPreference
android:key="nfc_foreground" android:key="nfc_foreground"
android:title="@string/nfc_payment_use_default" android:title="@string/nfc_payment_use_default"
settings:controller="com.android.settings.nfc.NfcForegroundPreferenceController"/> android:dialogTitle="@string/nfc_payment_use_default_dialog"
settings:controller="com.android.settings.nfc.NfcForegroundPreferenceController"
android:entries="@array/nfc_payment_favor"
android:entryValues="@array/nfc_payment_favor_values" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2022 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.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Layout;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AlignmentSpan;
import android.text.style.BulletSpan;
import android.text.style.ClickableSpan;
import android.text.style.RelativeSizeSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* DefaultPaymentSettings handles the NFC default payment app selection.
*/
public class DefaultPaymentSettings extends DefaultAppPickerFragment {
public static final String TAG = "DefaultPaymentSettings";
private PaymentBackend mPaymentBackend;
private List<PaymentAppInfo> mAppInfos;
private Preference mFooterPreference;
@Override
public int getMetricsCategory() {
return SettingsEnums.NFC_PAYMENT;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.nfc_default_payment_settings;
}
@Override
protected String getDefaultKey() {
PaymentAppInfo defaultAppInfo = mPaymentBackend.getDefaultApp();
if (defaultAppInfo != null) {
return defaultAppInfo.componentName.flattenToString() + " "
+ defaultAppInfo.userHandle.getIdentifier();
}
return null;
}
@Override
protected boolean setDefaultKey(String key) {
String[] keys = key.split(" ");
if (keys.length >= 2) {
mPaymentBackend.setDefaultPaymentApp(ComponentName.unflattenFromString(keys[0]),
Integer.parseInt(keys[1]));
}
return true;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mPaymentBackend = new PaymentBackend(getActivity());
mAppInfos = mPaymentBackend.getPaymentAppInfos();
}
@Override
protected void addStaticPreferences(PreferenceScreen screen) {
if (mFooterPreference == null) {
setupFooterPreference();
}
screen.addPreference(mFooterPreference);
}
@Override
public void onResume() {
super.onResume();
mPaymentBackend.onResume();
}
@Override
public void onPause() {
super.onPause();
mPaymentBackend.onPause();
}
/**
* Comparator for NfcPaymentCandidateInfo.
*/
public class NfcPaymentCandidateInfoComparator implements Comparator<NfcPaymentCandidateInfo> {
/**
* Compare the NfcPaymentCandidateInfo by the label string.
*/
public int compare(NfcPaymentCandidateInfo obj1, NfcPaymentCandidateInfo obj2) {
if (obj1.loadLabel() == obj2.loadLabel()) {
return 0;
}
if (obj1.loadLabel() == null) {
return -1;
}
if (obj2.loadLabel() == null) {
return 1;
}
return obj1.loadLabel().toString().compareTo(obj2.loadLabel().toString());
}
}
@Override
public void bindPreferenceExtra(SelectorWithWidgetPreference pref, String key,
CandidateInfo info, String defaultKey, String systemDefaultKey) {
final NfcPaymentCandidateInfo candidateInfo = (NfcPaymentCandidateInfo) info;
if (candidateInfo.isManagedProfile()) {
pref.setSummary("Work");
}
}
@Override
protected List<? extends CandidateInfo> getCandidates() {
final List<NfcPaymentCandidateInfo> candidates = new ArrayList<>();
for (PaymentAppInfo appInfo: mAppInfos) {
UserManager um = getContext().createContextAsUser(
appInfo.userHandle, /*flags=*/0).getSystemService(UserManager.class);
boolean isManagedProfile = um.isManagedProfile(appInfo.userHandle.getIdentifier());
CharSequence label;
label = appInfo.label;
candidates.add(new NfcPaymentCandidateInfo(
appInfo.componentName.flattenToString(),
label,
appInfo.icon,
appInfo.userHandle.getIdentifier(),
isManagedProfile));
}
Collections.sort(candidates, new NfcPaymentCandidateInfoComparator());
return candidates;
}
@VisibleForTesting
class NfcPaymentCandidateInfo extends CandidateInfo {
private final String mKey;
private final CharSequence mLabel;
private final Drawable mDrawable;
private final int mUserId;
private final boolean mIsManagedProfile;
NfcPaymentCandidateInfo(String key, CharSequence label, Drawable drawable, int userId,
boolean isManagedProfile) {
super(true /* enabled */);
mKey = key;
mLabel = label;
mDrawable = drawable;
mUserId = userId;
mIsManagedProfile = isManagedProfile;
}
@Override
public CharSequence loadLabel() {
return mLabel;
}
@Override
public Drawable loadIcon() {
return mDrawable;
}
@Override
public String getKey() {
return mKey + " " + mUserId;
}
public boolean isManagedProfile() {
return mIsManagedProfile;
}
}
@Override
protected CharSequence getConfirmationMessage(CandidateInfo appInfo) {
if (appInfo == null) {
return null;
}
NfcPaymentCandidateInfo paymentInfo = (NfcPaymentCandidateInfo) appInfo;
UserManager um = getContext().createContextAsUser(UserHandle.of(paymentInfo.mUserId),
/*flags=*/0).getSystemService(UserManager.class);
boolean isManagedProfile = um.isManagedProfile(paymentInfo.mUserId);
if (!isManagedProfile) {
return null;
}
final String title = getContext().getString(
R.string.nfc_default_payment_workapp_confirmation_title);
final String messageTitle = getContext().getString(
R.string.nfc_default_payment_workapp_confirmation_message_title);
final String messageOne = getContext().getString(
R.string.nfc_default_payment_workapp_confirmation_message_1);
final String messageTwo = getContext().getString(
R.string.nfc_default_payment_workapp_confirmation_message_2);
final SpannableString titleString = new SpannableString(title);
final SpannableString messageString = new SpannableString(messageTitle);
final SpannableString oneString = new SpannableString(messageOne);
final SpannableString twoString = new SpannableString(messageTwo);
titleString.setSpan(new RelativeSizeSpan(1.5f), 0, title.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
titleString.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0,
title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
messageString.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0,
messageTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
oneString.setSpan(new BulletSpan(20), 0, messageOne.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
twoString.setSpan(new BulletSpan(20), 0, messageTwo.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return TextUtils.concat(titleString, "\n\n", messageString, "\n\n", oneString, "\n",
twoString);
}
private void setupFooterPreference() {
final String textNfcDefaultPaymentFooter = getResources().getString(
R.string.nfc_default_payment_footer);
final String textMoreDetails = getResources().getString(R.string.nfc_more_details);
final SpannableString spannableString = new SpannableString(
textNfcDefaultPaymentFooter + System.lineSeparator()
+ System.lineSeparator() + textMoreDetails);
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Intent howItWorksIntent = new Intent(getActivity(), HowItWorks.class);
startActivity(howItWorksIntent);
}
};
if (textNfcDefaultPaymentFooter != null && textMoreDetails != null) {
spannableString.setSpan(clickableSpan, textNfcDefaultPaymentFooter.length() + 1,
textNfcDefaultPaymentFooter.length() + textMoreDetails.length() + 2,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mFooterPreference = new FooterPreference(getContext());
mFooterPreference.setLayoutResource(R.layout.preference_footer);
mFooterPreference.setTitle(spannableString);
mFooterPreference.setSelectable(false);
mFooterPreference.setIcon(R.drawable.ic_info_outline_24dp);
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.nfc;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.List;
/**
* NfcDefaultPaymentPreferenceController shows an app icon and text summary for current selected
* default payment, and links to the nfc default payment selection page.
*/
public class NfcDefaultPaymentPreferenceController extends DefaultAppPreferenceController implements
PaymentBackend.Callback, LifecycleObserver, OnResume, OnPause {
private static final String TAG = "NfcDefaultPaymentController";
private static final String KEY = "nfc_payment_app";
private PaymentBackend mPaymentBackend;
private Preference mPreference;
private Context mContext;
public NfcDefaultPaymentPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
mContext = context;
mPaymentBackend = new PaymentBackend(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public boolean isAvailable() {
final PackageManager pm = mContext.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
return false;
}
if (NfcAdapter.getDefaultAdapter(mContext) == null) {
return false;
}
if (mPaymentBackend == null) {
mPaymentBackend = new PaymentBackend(mContext);
}
final List<PaymentAppInfo> appInfos = mPaymentBackend.getPaymentAppInfos();
return (appInfos != null && !appInfos.isEmpty())
? true
: false;
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public void onResume() {
if (mPaymentBackend != null) {
mPaymentBackend.registerCallback(this);
mPaymentBackend.onResume();
}
}
@Override
public void onPause() {
if (mPaymentBackend != null) {
mPaymentBackend.unregisterCallback(this);
mPaymentBackend.onPause();
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
mPreference = screen.findPreference(getPreferenceKey());
super.displayPreference(screen);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
preference.setIconSpaceReserved(true);
}
@Override
public void onPaymentAppsChanged() {
updateState(mPreference);
}
/**
* PaymentDefaultAppInfo is used to store the default payment app info.
*/
public static class PaymentDefaultAppInfo extends DefaultAppInfo {
public PaymentAppInfo mInfo;
public PaymentDefaultAppInfo(Context context, PackageManager pm, int userId,
PaymentAppInfo info) {
super(context, pm, userId, info.componentName);
mInfo = info;
}
@Override
public Drawable loadIcon() {
return mInfo.icon;
}
}
@Override
protected DefaultAppInfo getDefaultAppInfo() {
if (mPaymentBackend == null) {
return null;
}
final PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp();
if (defaultApp != null) {
return new PaymentDefaultAppInfo(mContext, mPackageManager,
defaultApp.userHandle.getIdentifier(), defaultApp);
}
return null;
}
}

View File

@@ -16,11 +16,9 @@ package com.android.settings.nfc;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.text.TextUtils;
import androidx.preference.DropDownPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
@@ -36,13 +34,18 @@ public class NfcForegroundPreferenceController extends BasePreferenceController
PaymentBackend.Callback, Preference.OnPreferenceChangeListener, PaymentBackend.Callback, Preference.OnPreferenceChangeListener,
LifecycleObserver, OnStart, OnStop { LifecycleObserver, OnStart, OnStop {
private DropDownPreference mPreference; private ListPreference mPreference;
private PaymentBackend mPaymentBackend; private PaymentBackend mPaymentBackend;
private MetricsFeatureProvider mMetricsFeatureProvider; private MetricsFeatureProvider mMetricsFeatureProvider;
private final String[] mListValues;
private final String[] mListEntries;
public NfcForegroundPreferenceController(Context context, String key) { public NfcForegroundPreferenceController(Context context, String key) {
super(context, key); super(context, key);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mListValues = context.getResources().getStringArray(R.array.nfc_payment_favor_values);
mListEntries = context.getResources().getStringArray(R.array.nfc_payment_favor);
} }
public void setPaymentBackend(PaymentBackend backend) { public void setPaymentBackend(PaymentBackend backend) {
@@ -78,21 +81,6 @@ public class NfcForegroundPreferenceController extends BasePreferenceController
: UNSUPPORTED_ON_DEVICE; : UNSUPPORTED_ON_DEVICE;
} }
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (mPreference == null) {
return;
}
mPreference.setEntries(new CharSequence[]{
mContext.getText(R.string.nfc_payment_favor_open),
mContext.getText(R.string.nfc_payment_favor_default)
});
mPreference.setEntryValues(new CharSequence[]{"1", "0"});
}
@Override @Override
public void onPaymentAppsChanged() { public void onPaymentAppsChanged() {
updateState(mPreference); updateState(mPreference);
@@ -100,26 +88,29 @@ public class NfcForegroundPreferenceController extends BasePreferenceController
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
if (preference instanceof DropDownPreference) {
((DropDownPreference) preference).setValue(
mPaymentBackend.isForegroundMode() ? "1" : "0");
}
super.updateState(preference); super.updateState(preference);
if (!(preference instanceof ListPreference)) {
return;
}
final ListPreference listPreference = (ListPreference) preference;
listPreference.setIconSpaceReserved(true);
listPreference.setValue(mListValues[mPaymentBackend.isForegroundMode() ? 1 : 0]);
} }
@Override @Override
public CharSequence getSummary() { public CharSequence getSummary() {
return mPreference.getEntry(); return mListEntries[mPaymentBackend.isForegroundMode() ? 1 : 0];
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!(preference instanceof DropDownPreference)) { if (!(preference instanceof ListPreference)) {
return false; return false;
} }
final DropDownPreference pref = (DropDownPreference) preference;
final ListPreference listPreference = (ListPreference) preference;
final String newValueString = (String) newValue; final String newValueString = (String) newValue;
pref.setSummary(pref.getEntries()[pref.findIndexOfValue(newValueString)]); listPreference.setSummary(mListEntries[listPreference.findIndexOfValue(newValueString)]);
final boolean foregroundMode = Integer.parseInt(newValueString) != 0; final boolean foregroundMode = Integer.parseInt(newValueString) != 0;
mPaymentBackend.setForegroundMode(foregroundMode); mPaymentBackend.setForegroundMode(foregroundMode);
mMetricsFeatureProvider.action(mContext, mMetricsFeatureProvider.action(mContext,
@@ -127,12 +118,4 @@ public class NfcForegroundPreferenceController extends BasePreferenceController
: SettingsEnums.ACTION_NFC_PAYMENT_ALWAYS_SETTING); : SettingsEnums.ACTION_NFC_PAYMENT_ALWAYS_SETTING);
return true; return true;
} }
@Override
public void updateNonIndexableKeys(List<String> keys) {
final String key = getPreferenceKey();
if (!TextUtils.isEmpty(key)) {
keys.add(key);
}
}
} }

View File

@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.nfc.cardemulation.ApduServiceInfo; import android.nfc.cardemulation.ApduServiceInfo;
import android.nfc.cardemulation.CardEmulation; import android.nfc.cardemulation.CardEmulation;
@@ -50,6 +51,7 @@ public class PaymentBackend {
public ComponentName componentName; public ComponentName componentName;
public ComponentName settingsComponent; public ComponentName settingsComponent;
public UserHandle userHandle; public UserHandle userHandle;
public Drawable icon;
} }
/** /**
@@ -131,6 +133,7 @@ public class PaymentBackend {
appInfo.settingsComponent = null; appInfo.settingsComponent = null;
} }
appInfo.description = service.getDescription(); appInfo.description = service.getDescription();
appInfo.icon = pm.getUserBadgedIcon(service.loadIcon(pm), appInfo.userHandle);
appInfos.add(appInfo); appInfos.add(appInfo);
} }

View File

@@ -18,15 +18,11 @@ package com.android.settings.nfc;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -37,8 +33,12 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
@SearchIndexable @SearchIndexable
public class PaymentSettings extends DashboardFragment { public class PaymentSettings extends DashboardFragment {
@@ -61,13 +61,24 @@ public class PaymentSettings extends DashboardFragment {
return R.xml.nfc_payment_settings; return R.xml.nfc_payment_settings;
} }
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getSettingsLifecycle());
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new NfcDefaultPaymentPreferenceController(context, lifecycle));
return controllers;
}
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
mPaymentBackend = new PaymentBackend(getActivity()); mPaymentBackend = new PaymentBackend(getActivity());
setHasOptionsMenu(true);
use(NfcPaymentPreferenceController.class).setPaymentBackend(mPaymentBackend);
use(NfcForegroundPreferenceController.class).setPaymentBackend(mPaymentBackend); use(NfcForegroundPreferenceController.class).setPaymentBackend(mPaymentBackend);
} }
@@ -93,15 +104,6 @@ public class PaymentSettings extends DashboardFragment {
mPaymentBackend.onPause(); mPaymentBackend.onPause();
} }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
MenuItem menuItem = menu.add(R.string.nfc_payment_how_it_works);
Intent howItWorksIntent = new Intent(getActivity(), HowItWorks.class);
menuItem.setIntent(howItWorksIntent);
menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
}
@VisibleForTesting @VisibleForTesting
boolean isShowEmptyImage(PreferenceScreen screen) { boolean isShowEmptyImage(PreferenceScreen screen) {
for (int i = 0; i < screen.getPreferenceCount(); i++) { for (int i = 0; i < screen.getPreferenceCount(); i++) {

View File

@@ -26,7 +26,7 @@ import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import androidx.preference.DropDownPreference; import androidx.preference.ListPreference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
@@ -39,7 +39,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList; import java.util.ArrayList;
@@ -56,7 +55,7 @@ public class NfcForegroundPreferenceControllerTest {
private PackageManager mManager; private PackageManager mManager;
private Context mContext; private Context mContext;
private DropDownPreference mPreference; private ListPreference mPreference;
private NfcForegroundPreferenceController mController; private NfcForegroundPreferenceController mController;
private FakeFeatureFactory mFakeFeatureFactory; private FakeFeatureFactory mFakeFeatureFactory;
@@ -67,7 +66,9 @@ public class NfcForegroundPreferenceControllerTest {
when(mContext.getPackageManager()).thenReturn(mManager); when(mContext.getPackageManager()).thenReturn(mManager);
mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new NfcForegroundPreferenceController(mContext, PREF_KEY); mController = new NfcForegroundPreferenceController(mContext, PREF_KEY);
mPreference = new DropDownPreference(mContext); mPreference = new ListPreference(mContext);
mPreference.setEntries(R.array.nfc_payment_favor);
mPreference.setEntryValues(R.array.nfc_payment_favor_values);
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
} }
@@ -146,17 +147,14 @@ public class NfcForegroundPreferenceControllerTest {
final CharSequence favorDefault = mContext.getText(R.string.nfc_payment_favor_default); final CharSequence favorDefault = mContext.getText(R.string.nfc_payment_favor_default);
final CharSequence favorOpen = mContext.getText(R.string.nfc_payment_favor_open); final CharSequence favorOpen = mContext.getText(R.string.nfc_payment_favor_open);
assertThat(mPreference.getEntry()).isEqualTo(favorDefault); mPreference.setValueIndex(1);
assertThat(mPreference.getSummary()).isEqualTo(favorDefault); mPreference.callChangeListener(mPreference.getEntryValues()[1]);
mPreference.setValueIndex(0);
mPreference.callChangeListener(mPreference.getEntryValues()[0]);
verify(mPaymentBackend).setForegroundMode(true); verify(mPaymentBackend).setForegroundMode(true);
assertThat(mPreference.getEntry()).isEqualTo(favorOpen); assertThat(mPreference.getEntry()).isEqualTo(favorOpen);
assertThat(mPreference.getSummary()).isEqualTo(favorOpen); assertThat(mPreference.getSummary()).isEqualTo(favorOpen);
mPreference.setValueIndex(1); mPreference.setValueIndex(0);
mPreference.callChangeListener(mPreference.getEntryValues()[1]); mPreference.callChangeListener(mPreference.getEntryValues()[0]);
verify(mPaymentBackend).setForegroundMode(false); verify(mPaymentBackend).setForegroundMode(false);
assertThat(mPreference.getEntry()).isEqualTo(favorDefault); assertThat(mPreference.getEntry()).isEqualTo(favorDefault);
assertThat(mPreference.getSummary()).isEqualTo(favorDefault); assertThat(mPreference.getSummary()).isEqualTo(favorDefault);
@@ -168,14 +166,14 @@ public class NfcForegroundPreferenceControllerTest {
mController.displayPreference(mScreen); mController.displayPreference(mScreen);
mController.onPaymentAppsChanged(); mController.onPaymentAppsChanged();
mPreference.setValueIndex(0); mPreference.setValueIndex(1);
mPreference.callChangeListener(mPreference.getEntryValues()[0]); mPreference.callChangeListener(mPreference.getEntryValues()[1]);
verify(mPaymentBackend).setForegroundMode(true); verify(mPaymentBackend).setForegroundMode(true);
verify(mFakeFeatureFactory.metricsFeatureProvider).action(mContext, verify(mFakeFeatureFactory.metricsFeatureProvider).action(mContext,
SettingsEnums.ACTION_NFC_PAYMENT_FOREGROUND_SETTING); SettingsEnums.ACTION_NFC_PAYMENT_FOREGROUND_SETTING);
mPreference.setValueIndex(1); mPreference.setValueIndex(0);
mPreference.callChangeListener(mPreference.getEntryValues()[1]); mPreference.callChangeListener(mPreference.getEntryValues()[0]);
verify(mPaymentBackend).setForegroundMode(false); verify(mPaymentBackend).setForegroundMode(false);
verify(mFakeFeatureFactory.metricsFeatureProvider).action(mContext, verify(mFakeFeatureFactory.metricsFeatureProvider).action(mContext,
SettingsEnums.ACTION_NFC_PAYMENT_ALWAYS_SETTING); SettingsEnums.ACTION_NFC_PAYMENT_ALWAYS_SETTING);

View File

@@ -53,7 +53,7 @@ import java.util.List;
@Config(shadows = {PaymentSettingsTest.ShadowPaymentBackend.class, ShadowNfcAdapter.class}) @Config(shadows = {PaymentSettingsTest.ShadowPaymentBackend.class, ShadowNfcAdapter.class})
public class PaymentSettingsTest { public class PaymentSettingsTest {
static final String PAYMENT_KEY = "nfc_payment"; static final String PAYMENT_KEY = "nfc_payment_app";
static final String FOREGROUND_KEY = "nfc_foreground"; static final String FOREGROUND_KEY = "nfc_foreground";
private Context mContext; private Context mContext;