Home | History | Annotate | Download | only in cardemulation
      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         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
    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
    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 
    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 
    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
    221         public int getCount() {
    222             return mList.size();
    223         }
    224 
    225         @Override
    226         public Object getItem(int position) {
    227             return mList.get(position);
    228         }
    229 
    230         @Override
    231         public long getItemId(int position) {
    232             return position;
    233         }
    234 
    235         @Override
    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;
    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 }