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