1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.nfc;
18 
19 import android.app.settings.SettingsEnums;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.graphics.drawable.Drawable;
24 import android.os.UserHandle;
25 import android.os.UserManager;
26 import android.text.Layout;
27 import android.text.SpannableString;
28 import android.text.Spanned;
29 import android.text.TextUtils;
30 import android.text.style.AlignmentSpan;
31 import android.text.style.BulletSpan;
32 import android.text.style.RelativeSizeSpan;
33 
34 import androidx.annotation.VisibleForTesting;
35 import androidx.preference.PreferenceScreen;
36 
37 import com.android.settings.R;
38 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
39 import com.android.settings.nfc.PaymentBackend.PaymentAppInfo;
40 import com.android.settingslib.widget.CandidateInfo;
41 import com.android.settingslib.widget.FooterPreference;
42 import com.android.settingslib.widget.SelectorWithWidgetPreference;
43 
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.List;
48 
49 /**
50  * DefaultPaymentSettings handles the NFC default payment app selection.
51  */
52 public class DefaultPaymentSettings extends DefaultAppPickerFragment {
53     public static final String TAG = "DefaultPaymentSettings";
54 
55     private PaymentBackend mPaymentBackend;
56     private List<PaymentAppInfo> mAppInfos;
57     private FooterPreference mFooterPreference;
58 
59     @Override
getMetricsCategory()60     public int getMetricsCategory() {
61         return SettingsEnums.NFC_DEFAULT_PAYMENT;
62     }
63 
64     @Override
getPreferenceScreenResId()65     protected int getPreferenceScreenResId() {
66         return R.xml.nfc_default_payment_settings;
67     }
68 
69     @Override
getDefaultKey()70     protected String getDefaultKey() {
71         PaymentAppInfo defaultAppInfo = mPaymentBackend.getDefaultApp();
72         if (defaultAppInfo != null) {
73             return defaultAppInfo.componentName.flattenToString() + " "
74                     + defaultAppInfo.userHandle.getIdentifier();
75         }
76         return null;
77     }
78 
79     @Override
setDefaultKey(String key)80     protected boolean setDefaultKey(String key) {
81         String[] keys = key.split(" ");
82         if (keys.length >= 2) {
83             mPaymentBackend.setDefaultPaymentApp(ComponentName.unflattenFromString(keys[0]),
84                     Integer.parseInt(keys[1]));
85         }
86         return true;
87     }
88 
89     @Override
onAttach(Context context)90     public void onAttach(Context context) {
91         super.onAttach(context);
92         mPaymentBackend = new PaymentBackend(getActivity());
93         mAppInfos = mPaymentBackend.getPaymentAppInfos();
94     }
95 
96     @Override
addStaticPreferences(PreferenceScreen screen)97     protected void addStaticPreferences(PreferenceScreen screen) {
98         if (mFooterPreference == null) {
99             setupFooterPreference();
100         }
101         screen.addPreference(mFooterPreference);
102     }
103 
104     @Override
onResume()105     public void onResume() {
106         super.onResume();
107         mPaymentBackend.onResume();
108     }
109 
110     @Override
onPause()111     public void onPause() {
112         super.onPause();
113         mPaymentBackend.onPause();
114     }
115 
116     /**
117      * Comparator for NfcPaymentCandidateInfo.
118      */
119     public class NfcPaymentCandidateInfoComparator implements Comparator<NfcPaymentCandidateInfo> {
120         /**
121          * Compare the NfcPaymentCandidateInfo by the label string.
122          */
compare(NfcPaymentCandidateInfo obj1, NfcPaymentCandidateInfo obj2)123         public int compare(NfcPaymentCandidateInfo obj1, NfcPaymentCandidateInfo obj2) {
124             if (obj1.loadLabel() == obj2.loadLabel()) {
125                 return 0;
126             }
127             if (obj1.loadLabel() == null) {
128                 return -1;
129             }
130             if (obj2.loadLabel() == null) {
131                 return 1;
132             }
133             return obj1.loadLabel().toString().compareTo(obj2.loadLabel().toString());
134         }
135     }
136 
137     @Override
bindPreferenceExtra(SelectorWithWidgetPreference pref, String key, CandidateInfo info, String defaultKey, String systemDefaultKey)138     public void bindPreferenceExtra(SelectorWithWidgetPreference pref, String key,
139             CandidateInfo info, String defaultKey, String systemDefaultKey) {
140         final NfcPaymentCandidateInfo candidateInfo = (NfcPaymentCandidateInfo) info;
141         if (candidateInfo.isManagedProfile()) {
142             final String textWork = getContext().getString(R.string.nfc_work_text);
143             pref.setSummary(textWork);
144         }
145     }
146 
147     @Override
getCandidates()148     protected List<? extends CandidateInfo> getCandidates() {
149         final List<NfcPaymentCandidateInfo> candidates = new ArrayList<>();
150         for (PaymentAppInfo appInfo: mAppInfos) {
151             UserManager um = getContext().createContextAsUser(
152                     appInfo.userHandle, /*flags=*/0).getSystemService(UserManager.class);
153             boolean isManagedProfile = um.isManagedProfile(appInfo.userHandle.getIdentifier());
154 
155             CharSequence label;
156             label = appInfo.label;
157             candidates.add(new NfcPaymentCandidateInfo(
158                     appInfo.componentName.flattenToString(),
159                     label,
160                     appInfo.icon,
161                     appInfo.userHandle.getIdentifier(),
162                     isManagedProfile));
163         }
164         Collections.sort(candidates, new NfcPaymentCandidateInfoComparator());
165         return candidates;
166     }
167 
168     @VisibleForTesting
169     class NfcPaymentCandidateInfo extends CandidateInfo {
170         private final String mKey;
171         private final CharSequence mLabel;
172         private final Drawable mDrawable;
173         private final int mUserId;
174         private final boolean mIsManagedProfile;
175 
NfcPaymentCandidateInfo(String key, CharSequence label, Drawable drawable, int userId, boolean isManagedProfile)176         NfcPaymentCandidateInfo(String key, CharSequence label, Drawable drawable, int userId,
177                 boolean isManagedProfile) {
178             super(true /* enabled */);
179             mKey = key;
180             mLabel = label;
181             mDrawable = drawable;
182             mUserId = userId;
183             mIsManagedProfile = isManagedProfile;
184         }
185 
186         @Override
loadLabel()187         public CharSequence loadLabel() {
188             return mLabel;
189         }
190 
191         @Override
loadIcon()192         public Drawable loadIcon() {
193             return mDrawable;
194         }
195 
196         @Override
getKey()197         public String getKey() {
198             return mKey + " " + mUserId;
199         }
200 
isManagedProfile()201         public boolean isManagedProfile() {
202             return mIsManagedProfile;
203         }
204     }
205 
206     @Override
getConfirmationMessage(CandidateInfo appInfo)207     protected CharSequence getConfirmationMessage(CandidateInfo appInfo) {
208         if (appInfo == null) {
209             return null;
210         }
211         NfcPaymentCandidateInfo paymentInfo = (NfcPaymentCandidateInfo) appInfo;
212         UserManager um = getContext().createContextAsUser(UserHandle.of(paymentInfo.mUserId),
213                 /*flags=*/0).getSystemService(UserManager.class);
214         boolean isManagedProfile = um.isManagedProfile(paymentInfo.mUserId);
215         if (!isManagedProfile) {
216             return null;
217         }
218 
219         final String title = getContext().getString(
220                 R.string.nfc_default_payment_workapp_confirmation_title);
221         final String messageTitle = getContext().getString(
222                 R.string.nfc_default_payment_workapp_confirmation_message_title);
223         final String messageOne = getContext().getString(
224                 R.string.nfc_default_payment_workapp_confirmation_message_1);
225         final String messageTwo = getContext().getString(
226                 R.string.nfc_default_payment_workapp_confirmation_message_2);
227         final SpannableString titleString = new SpannableString(title);
228         final SpannableString messageString = new SpannableString(messageTitle);
229         final SpannableString oneString = new SpannableString(messageOne);
230         final SpannableString twoString = new SpannableString(messageTwo);
231 
232         titleString.setSpan(new RelativeSizeSpan(1.5f), 0, title.length(),
233                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
234         titleString.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0,
235                 title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
236         messageString.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0,
237                 messageTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
238         oneString.setSpan(new BulletSpan(20), 0, messageOne.length(),
239                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
240         twoString.setSpan(new BulletSpan(20), 0, messageTwo.length(),
241                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
242 
243         return TextUtils.concat(titleString, "\n\n", messageString, "\n\n", oneString, "\n",
244                 twoString);
245     }
246 
setupFooterPreference()247     private void setupFooterPreference() {
248         mFooterPreference = new FooterPreference(getContext());
249         mFooterPreference.setTitle(getResources().getString(R.string.nfc_default_payment_footer));
250         mFooterPreference.setIcon(R.drawable.ic_info_outline_24dp);
251         mFooterPreference.setLearnMoreAction(v -> {
252             final Intent howItWorksIntent = new Intent(getActivity(), HowItWorks.class);
253             getContext().startActivity(howItWorksIntent);
254         });
255     }
256 }
257