1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.settings.nfc; 15 16 import android.app.Dialog; 17 import android.content.ActivityNotFoundException; 18 import android.content.Context; 19 import android.content.DialogInterface; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.nfc.NfcAdapter; 23 import android.util.Log; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.BaseAdapter; 28 import android.widget.CompoundButton; 29 import android.widget.ImageView; 30 import android.widget.RadioButton; 31 32 import androidx.appcompat.app.AlertDialog.Builder; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceScreen; 35 import androidx.preference.PreferenceViewHolder; 36 37 import com.android.settings.R; 38 import com.android.settings.core.BasePreferenceController; 39 import com.android.settings.nfc.PaymentBackend.PaymentAppInfo; 40 import com.android.settingslib.core.lifecycle.LifecycleObserver; 41 import com.android.settingslib.core.lifecycle.events.OnStart; 42 import com.android.settingslib.core.lifecycle.events.OnStop; 43 44 import java.util.List; 45 46 public class NfcPaymentPreferenceController extends BasePreferenceController implements 47 PaymentBackend.Callback, View.OnClickListener, NfcPaymentPreference.Listener, 48 LifecycleObserver, OnStart, OnStop { 49 50 private static final String TAG = "NfcPaymentController"; 51 52 private final NfcPaymentAdapter mAdapter; 53 private PaymentBackend mPaymentBackend; 54 private NfcPaymentPreference mPreference; 55 private ImageView mSettingsButtonView; 56 NfcPaymentPreferenceController(Context context, String key)57 public NfcPaymentPreferenceController(Context context, String key) { 58 super(context, key); 59 mAdapter = new NfcPaymentAdapter(context); 60 } 61 setPaymentBackend(PaymentBackend backend)62 public void setPaymentBackend(PaymentBackend backend) { 63 mPaymentBackend = backend; 64 } 65 66 @Override onStart()67 public void onStart() { 68 if (mPaymentBackend != null) { 69 mPaymentBackend.registerCallback(this); 70 } 71 } 72 73 @Override onStop()74 public void onStop() { 75 if (mPaymentBackend != null) { 76 mPaymentBackend.unregisterCallback(this); 77 } 78 } 79 80 @Override getAvailabilityStatus()81 public int getAvailabilityStatus() { 82 final PackageManager pm = mContext.getPackageManager(); 83 if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) { 84 return UNSUPPORTED_ON_DEVICE; 85 } 86 if (NfcAdapter.getDefaultAdapter(mContext) == null) { 87 return UNSUPPORTED_ON_DEVICE; 88 } 89 if (mPaymentBackend == null) { 90 mPaymentBackend = new PaymentBackend(mContext); 91 } 92 final List<PaymentAppInfo> appInfos = mPaymentBackend.getPaymentAppInfos(); 93 return (appInfos != null && !appInfos.isEmpty()) 94 ? AVAILABLE 95 : UNSUPPORTED_ON_DEVICE; 96 } 97 98 @Override displayPreference(PreferenceScreen screen)99 public void displayPreference(PreferenceScreen screen) { 100 super.displayPreference(screen); 101 mPreference = screen.findPreference(getPreferenceKey()); 102 if (mPreference != null) { 103 mPreference.initialize(this); 104 } 105 } 106 107 @Override onBindViewHolder(PreferenceViewHolder view)108 public void onBindViewHolder(PreferenceViewHolder view) { 109 mSettingsButtonView = (ImageView) view.findViewById(R.id.settings_button); 110 mSettingsButtonView.setOnClickListener(this); 111 112 updateSettingsVisibility(); 113 } 114 115 @Override updateState(Preference preference)116 public void updateState(Preference preference) { 117 final List<PaymentAppInfo> appInfos = mPaymentBackend.getPaymentAppInfos(); 118 if (appInfos != null) { 119 final PaymentAppInfo[] apps = appInfos.toArray(new PaymentAppInfo[appInfos.size()]); 120 mAdapter.updateApps(apps); 121 } 122 super.updateState(preference); 123 updateSettingsVisibility(); 124 } 125 126 @Override getSummary()127 public CharSequence getSummary() { 128 final PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp(); 129 if (defaultApp != null) { 130 return defaultApp.label; 131 } else { 132 return mContext.getText(R.string.nfc_payment_default_not_set); 133 } 134 } 135 136 @Override onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)137 public void onPrepareDialogBuilder(Builder builder, 138 DialogInterface.OnClickListener listener) { 139 builder.setSingleChoiceItems(mAdapter, 0, listener); 140 } 141 142 @Override onPaymentAppsChanged()143 public void onPaymentAppsChanged() { 144 updateState(mPreference); 145 } 146 147 @Override onClick(View view)148 public void onClick(View view) { 149 final PaymentAppInfo defaultAppInfo = mPaymentBackend.getDefaultApp(); 150 if (defaultAppInfo != null && defaultAppInfo.settingsComponent != null) { 151 final Intent settingsIntent = new Intent(Intent.ACTION_MAIN); 152 settingsIntent.setComponent(defaultAppInfo.settingsComponent); 153 settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 154 try { 155 mContext.startActivity(settingsIntent); 156 } catch (ActivityNotFoundException e) { 157 Log.e(TAG, "Settings activity not found."); 158 } 159 } 160 } 161 updateSettingsVisibility()162 private void updateSettingsVisibility() { 163 if (mSettingsButtonView != null) { 164 final PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp(); 165 if (defaultApp == null || defaultApp.settingsComponent == null) { 166 mSettingsButtonView.setVisibility(View.GONE); 167 } else { 168 mSettingsButtonView.setVisibility(View.VISIBLE); 169 } 170 } 171 } 172 173 private class NfcPaymentAdapter extends BaseAdapter implements 174 CompoundButton.OnCheckedChangeListener, View.OnClickListener { 175 private final LayoutInflater mLayoutInflater; 176 177 // Only modified on UI thread 178 private PaymentAppInfo[] appInfos; 179 NfcPaymentAdapter(Context context)180 public NfcPaymentAdapter(Context context) { 181 mLayoutInflater = (LayoutInflater) context.getSystemService( 182 Context.LAYOUT_INFLATER_SERVICE); 183 } 184 updateApps(PaymentAppInfo[] appInfos)185 public void updateApps(PaymentAppInfo[] appInfos) { 186 // Clone app infos, only add an application label 187 this.appInfos = appInfos; 188 notifyDataSetChanged(); 189 } 190 191 @Override getCount()192 public int getCount() { 193 return (appInfos != null) ? appInfos.length : 0; 194 } 195 196 @Override getItem(int i)197 public PaymentAppInfo getItem(int i) { 198 return appInfos[i]; 199 } 200 201 @Override getItemId(int i)202 public long getItemId(int i) { 203 return appInfos[i].componentName.hashCode(); 204 } 205 206 @Override getView(int position, View convertView, ViewGroup parent)207 public View getView(int position, View convertView, ViewGroup parent) { 208 ViewHolder holder; 209 final PaymentAppInfo appInfo = appInfos[position]; 210 if (convertView == null) { 211 convertView = mLayoutInflater.inflate( 212 R.layout.nfc_payment_option, parent, false); 213 holder = new ViewHolder(); 214 holder.radioButton = convertView.findViewById(R.id.button); 215 convertView.setTag(holder); 216 } else { 217 holder = (ViewHolder) convertView.getTag(); 218 } 219 220 // Prevent checked callback getting called on recycled views 221 holder.radioButton.setOnCheckedChangeListener(null); 222 holder.radioButton.setChecked(appInfo.isDefault); 223 holder.radioButton.setContentDescription(appInfo.label); 224 holder.radioButton.setOnCheckedChangeListener(this); 225 holder.radioButton.setTag(appInfo); 226 holder.radioButton.setText(appInfo.label); 227 return convertView; 228 } 229 230 private class ViewHolder { 231 public RadioButton radioButton; 232 } 233 234 @Override onCheckedChanged(CompoundButton compoundButton, boolean b)235 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 236 PaymentAppInfo appInfo = (PaymentAppInfo) compoundButton.getTag(); 237 makeDefault(appInfo); 238 } 239 240 @Override onClick(View view)241 public void onClick(View view) { 242 PaymentAppInfo appInfo = (PaymentAppInfo) view.getTag(); 243 makeDefault(appInfo); 244 } 245 makeDefault(PaymentAppInfo appInfo)246 private void makeDefault(PaymentAppInfo appInfo) { 247 if (!appInfo.isDefault) { 248 mPaymentBackend.setDefaultPaymentApp(appInfo.componentName); 249 } 250 final Dialog dialog = mPreference.getDialog(); 251 if (dialog != null) { 252 dialog.dismiss(); 253 } 254 } 255 } 256 } 257