Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54dbe403 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "New UX for the NFC default payment settings"

parents 54769b55 9de30f08
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1596,4 +1596,15 @@
        <item>2</item>
        <item>4</item>
    </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>
+21 −1
Original line number Diff line number Diff line
@@ -7596,7 +7596,25 @@
    <string name="user_copy_apps_menu_title">Install available apps</string>
    <!-- NFC payment settings --><skip/>
    <!-- Title for NFC payment settings page [CHAR LIMIT=40] -->
    <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-->
    <string name="nfc_payment_how_it_works">How it works</string>
    <!-- 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 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 -->
    <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) -->
    <string name="nfc_payment_favor_default">Always</string>
    <!-- If open app supports TapPay, use that app instead of the default -->
+19 −0
Original line number Diff line number Diff line
<?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"/>
+9 −8
Original line number Diff line number Diff line
@@ -19,16 +19,17 @@
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/nfc_payment_settings_title">

    <com.android.settings.nfc.NfcPaymentPreference
        android:key="nfc_payment"
        android:title="@string/nfc_payment_default"
        android:dialogTitle="@string/nfc_payment_pay_with"
        android:widgetLayout="@layout/preference_widget_gear"
        settings:controller="com.android.settings.nfc.NfcPaymentPreferenceController"/>
    <com.android.settings.widget.GearPreference
        android:key="nfc_payment_app"
        android:title="@string/nfc_default_payment_settings_title"
        android:fragment="com.android.settings.nfc.DefaultPaymentSettings"/>

    <DropDownPreference
    <ListPreference
        android:key="nfc_foreground"
        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>
+278 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
Loading