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