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
17package com.android.nfc.cardemulation;
18
19import com.android.internal.R;
20import com.android.internal.app.AlertActivity;
21import com.android.internal.app.AlertController;
22
23import java.util.ArrayList;
24import java.util.List;
25
26import android.app.ActivityManager;
27import android.content.BroadcastReceiver;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.PackageManager.NameNotFoundException;
35import android.graphics.drawable.Drawable;
36import android.nfc.NfcAdapter;
37import android.nfc.cardemulation.ApduServiceInfo;
38import android.nfc.cardemulation.CardEmulation;
39import android.os.Bundle;
40import android.util.Log;
41import android.view.LayoutInflater;
42import android.view.View;
43import android.view.ViewGroup;
44import android.view.Window;
45import android.view.WindowManager;
46import android.widget.AdapterView;
47import android.widget.BaseAdapter;
48import android.widget.ImageView;
49import android.widget.ListView;
50import android.widget.TextView;
51
52public 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}