1 /* 2 * Copyright (C) 2013 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.nfc.cardemulation; 18 19 import com.android.internal.R; 20 import com.android.internal.app.AlertActivity; 21 import com.android.internal.app.AlertController; 22 23 import java.util.ArrayList; 24 import java.util.List; 25 26 import android.app.ActivityManager; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.graphics.drawable.Drawable; 36 import android.nfc.NfcAdapter; 37 import android.nfc.cardemulation.ApduServiceInfo; 38 import android.nfc.cardemulation.CardEmulation; 39 import android.os.Bundle; 40 import android.util.Log; 41 import android.view.LayoutInflater; 42 import android.view.View; 43 import android.view.ViewGroup; 44 import android.view.Window; 45 import android.view.WindowManager; 46 import android.widget.AdapterView; 47 import android.widget.BaseAdapter; 48 import android.widget.ImageView; 49 import android.widget.ListView; 50 import android.widget.TextView; 51 52 public class AppChooserActivity extends AlertActivity 53 implements AdapterView.OnItemClickListener { 54 55 static final String TAG = "AppChooserActivity"; 56 57 public static final String EXTRA_APDU_SERVICES = "services"; 58 public static final String EXTRA_CATEGORY = "category"; 59 public static final String EXTRA_FAILED_COMPONENT = "failed_component"; 60 61 private int mIconSize; 62 private ListView mListView; 63 private ListAdapter mListAdapter; 64 private CardEmulation mCardEmuManager; 65 private String mCategory; 66 67 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 68 @Override 69 public void onReceive(Context context, Intent intent) { 70 finish(); 71 } 72 }; 73 74 @Override 75 protected void onDestroy() { 76 super.onDestroy(); 77 unregisterReceiver(mReceiver); 78 } 79 80 protected void onCreate(Bundle savedInstanceState, String category, 81 ArrayList<ApduServiceInfo> options, ComponentName failedComponent) { 82 super.onCreate(savedInstanceState); 83 setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); 84 85 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 86 registerReceiver(mReceiver, filter); 87 88 if ((options == null || options.size() == 0) && failedComponent == null) { 89 Log.e(TAG, "No components passed in."); 90 finish(); 91 return; 92 } 93 94 95 mCategory = category; 96 boolean isPayment = CardEmulation.CATEGORY_PAYMENT.equals(mCategory); 97 98 final NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); 99 mCardEmuManager = CardEmulation.getInstance(adapter); 100 AlertController.AlertParams ap = mAlertParams; 101 102 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 103 mIconSize = am.getLauncherLargeIconSize(); 104 105 // Three cases: 106 // 1. Failed component and no alternatives: just an OK box 107 // 2. Failed component and alternatives: pick alternative 108 // 3. No failed component and alternatives: pick alternative 109 PackageManager pm = getPackageManager(); 110 111 CharSequence applicationLabel = "unknown"; 112 if (failedComponent != null) { 113 try { 114 ApplicationInfo info = pm.getApplicationInfo(failedComponent.getPackageName(), 0); 115 applicationLabel = info.loadLabel(pm); 116 } catch (NameNotFoundException e) { 117 } 118 119 } 120 if (options.size() == 0 && failedComponent != null) { 121 String formatString = getString(com.android.nfc.R.string.transaction_failure); 122 ap.mTitle = ""; 123 ap.mMessage = String.format(formatString, applicationLabel); 124 ap.mPositiveButtonText = getString(R.string.ok); 125 setupAlert(); 126 } else { 127 mListAdapter = new ListAdapter(this, options); 128 if (failedComponent != null) { 129 String formatString = getString(com.android.nfc.R.string.could_not_use_app); 130 ap.mTitle = String.format(formatString, applicationLabel); 131 ap.mNegativeButtonText = getString(R.string.cancel); 132 } else { 133 if (CardEmulation.CATEGORY_PAYMENT.equals(category)) { 134 ap.mTitle = getString(com.android.nfc.R.string.pay_with); 135 } else { 136 ap.mTitle = getString(com.android.nfc.R.string.complete_with); 137 } 138 } 139 ap.mView = getLayoutInflater().inflate(com.android.nfc.R.layout.cardemu_resolver, null); 140 141 mListView = (ListView) ap.mView.findViewById(com.android.nfc.R.id.resolver_list); 142 if (isPayment) { 143 mListView.setDivider(getResources().getDrawable(android.R.color.transparent)); 144 int height = (int) (getResources().getDisplayMetrics().density * 16); 145 mListView.setDividerHeight(height); 146 } else { 147 mListView.setPadding(0, 0, 0, 0); 148 } 149 mListView.setAdapter(mListAdapter); 150 mListView.setOnItemClickListener(this); 151 152 setupAlert(); 153 } 154 Window window = getWindow(); 155 window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 156 } 157 158 @Override 159 protected void onCreate(Bundle savedInstanceState) { 160 Intent intent = getIntent(); 161 ArrayList<ApduServiceInfo> services = intent.getParcelableArrayListExtra(EXTRA_APDU_SERVICES); 162 String category = intent.getStringExtra(EXTRA_CATEGORY); 163 ComponentName failedComponent = intent.getParcelableExtra(EXTRA_FAILED_COMPONENT); 164 onCreate(savedInstanceState, category, services, failedComponent); 165 } 166 167 @Override 168 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 169 DisplayAppInfo info = (DisplayAppInfo) mListAdapter.getItem(position); 170 mCardEmuManager.setDefaultForNextTap(info.serviceInfo.getComponent()); 171 Intent dialogIntent = new Intent(this, TapAgainDialog.class); 172 dialogIntent.putExtra(TapAgainDialog.EXTRA_CATEGORY, mCategory); 173 dialogIntent.putExtra(TapAgainDialog.EXTRA_APDU_SERVICE, info.serviceInfo); 174 startActivity(dialogIntent); 175 finish(); 176 } 177 178 final class DisplayAppInfo { 179 ApduServiceInfo serviceInfo; 180 CharSequence displayLabel; 181 Drawable displayIcon; 182 Drawable displayBanner; 183 184 public DisplayAppInfo(ApduServiceInfo serviceInfo, CharSequence label, Drawable icon, 185 Drawable banner) { 186 this.serviceInfo = serviceInfo; 187 displayIcon = icon; 188 displayLabel = label; 189 displayBanner = banner; 190 } 191 } 192 193 final class ListAdapter extends BaseAdapter { 194 private final LayoutInflater mInflater; 195 private final boolean mIsPayment; 196 private List<DisplayAppInfo> mList; 197 198 public ListAdapter(Context context, ArrayList<ApduServiceInfo> services) { 199 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 200 // For each component, get the corresponding app name and icon 201 PackageManager pm = getPackageManager(); 202 mList = new ArrayList<DisplayAppInfo>(); 203 mIsPayment = CardEmulation.CATEGORY_PAYMENT.equals(mCategory); 204 for (ApduServiceInfo service : services) { 205 CharSequence label = service.getDescription(); 206 if (label == null) label = service.loadLabel(pm); 207 Drawable icon = service.loadIcon(pm); 208 Drawable banner = null; 209 if (mIsPayment) { 210 banner = service.loadBanner(pm); 211 if (banner == null) { 212 Log.e(TAG, "Not showing " + label + " because no banner specified."); 213 continue; 214 } 215 } 216 DisplayAppInfo info = new DisplayAppInfo(service, label, icon, banner); 217 mList.add(info); 218 } 219 } 220 221 @Override 222 public int getCount() { 223 return mList.size(); 224 } 225 226 @Override 227 public Object getItem(int position) { 228 return mList.get(position); 229 } 230 231 @Override 232 public long getItemId(int position) { 233 return position; 234 } 235 236 @Override 237 public View getView(int position, View convertView, ViewGroup parent) { 238 View view; 239 if (convertView == null) { 240 if (mIsPayment) { 241 view = mInflater.inflate( 242 com.android.nfc.R.layout.cardemu_payment_item, parent, false); 243 } else { 244 view = mInflater.inflate( 245 com.android.nfc.R.layout.cardemu_item, parent, false); 246 } 247 final ViewHolder holder = new ViewHolder(view); 248 view.setTag(holder); 249 250 } else { 251 view = convertView; 252 } 253 254 final ViewHolder holder = (ViewHolder) view.getTag(); 255 DisplayAppInfo appInfo = mList.get(position); 256 if (mIsPayment) { 257 holder.banner.setImageDrawable(appInfo.displayBanner); 258 } else { 259 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams(); 260 lp.width = lp.height = mIconSize; 261 holder.icon.setImageDrawable(appInfo.displayIcon); 262 holder.text.setText(appInfo.displayLabel); 263 } 264 return view; 265 } 266 } 267 268 static class ViewHolder { 269 public TextView text; 270 public ImageView icon; 271 public ImageView banner; 272 public ViewHolder(View view) { 273 text = (TextView) view.findViewById(com.android.nfc.R.id.applabel); 274 icon = (ImageView) view.findViewById(com.android.nfc.R.id.appicon); 275 banner = (ImageView) view.findViewById(com.android.nfc.R.id.banner); 276 } 277 } 278 }