blob: 63191333e73c53fccae0b707fa964a04168ccc95 [file] [log] [blame]
Dan Morrill5df275b2010-08-11 12:19:19 -07001/*
2 * Copyright (C) 2010 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/*
18 * This file references fs_error.png, fs_good.png, fs_indeterminate.png,
19 * and fs_warning.png which are licensed under Creative Commons 3.0
20 * by fatcow.com.
21 * http://www.fatcow.com/free-icons/
22 * http://creativecommons.org/licenses/by/3.0/us/
23 */
Dan Morrill71351d82010-10-20 15:26:00 -070024
Dan Morrill5df275b2010-08-11 12:19:19 -070025package com.android.cts.verifier.features;
26
Brian Muramatsue1181872010-08-20 12:59:39 -070027import com.android.cts.verifier.PassFailButtons;
Dan Morrill5df275b2010-08-11 12:19:19 -070028import com.android.cts.verifier.R;
29
Dan Morrill5df275b2010-08-11 12:19:19 -070030import android.content.pm.FeatureInfo;
31import android.content.pm.PackageManager;
Dan Morrill71351d82010-10-20 15:26:00 -070032import android.os.Build;
Dan Morrill5df275b2010-08-11 12:19:19 -070033import android.os.Bundle;
34import android.view.View;
35import android.widget.ImageView;
36import android.widget.SimpleAdapter;
37import android.widget.TextView;
38
Brian Muramatsue1181872010-08-20 12:59:39 -070039import java.util.ArrayList;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.HashMap;
Brian Muramatsud8d37a42011-07-07 14:11:26 -070043import java.util.LinkedHashSet;
44import java.util.Set;
Brian Muramatsue1181872010-08-20 12:59:39 -070045
46public class FeatureSummaryActivity extends PassFailButtons.ListActivity {
Dan Morrill5df275b2010-08-11 12:19:19 -070047 /**
48 * Simple storage class for data about an Android feature.
49 */
50 static class Feature {
51 /**
52 * The name of the feature. Should be one of the PackageManager.FEATURE*
53 * constants.
54 */
55 public String name;
56
57 /**
58 * Indicates whether the field is present on the current device.
59 */
60 public boolean present;
61
62 /**
63 * Indicates whether the field is required for the current device.
64 */
65 public boolean required;
66
67 /**
68 * Constructor does not include 'present' because that's a detected
69 * value, and not set during creation.
Brian Muramatsude694442011-03-09 14:13:01 -080070 *
Dan Morrill5df275b2010-08-11 12:19:19 -070071 * @param name value for this.name
72 * @param required value for this.required
73 */
74 public Feature(String name, boolean required) {
75 this.name = name;
76 this.required = required;
77 this.present = false;
78 }
Brian Muramatsud8d37a42011-07-07 14:11:26 -070079
80 @Override
81 public boolean equals(Object o) {
82 if (this == o) {
83 return true;
84 } else if (o == null || !(o instanceof Feature)) {
85 return false;
86 } else {
87 Feature feature = (Feature) o;
88 return name.equals(feature.name);
89 }
90 }
91
92 @Override
93 public int hashCode() {
94 return name.hashCode();
95 }
Dan Morrill5df275b2010-08-11 12:19:19 -070096 }
97
Dan Morrill71351d82010-10-20 15:26:00 -070098 public static final Feature[] ALL_ECLAIR_FEATURES = {
Dan Morrill5df275b2010-08-11 12:19:19 -070099 new Feature(PackageManager.FEATURE_CAMERA, true),
100 new Feature(PackageManager.FEATURE_CAMERA_AUTOFOCUS, false),
101 new Feature(PackageManager.FEATURE_CAMERA_FLASH, false),
102 new Feature(PackageManager.FEATURE_LIVE_WALLPAPER, false),
Dan Morrill5df275b2010-08-11 12:19:19 -0700103 new Feature(PackageManager.FEATURE_SENSOR_LIGHT, false),
104 new Feature(PackageManager.FEATURE_SENSOR_PROXIMITY, false),
105 new Feature(PackageManager.FEATURE_TELEPHONY, false),
106 new Feature(PackageManager.FEATURE_TELEPHONY_CDMA, false),
107 new Feature(PackageManager.FEATURE_TELEPHONY_GSM, false),
Dan Morrill71351d82010-10-20 15:26:00 -0700108 };
109
Dan Morrill71351d82010-10-20 15:26:00 -0700110 public static final Feature[] ALL_FROYO_FEATURES = {
111 new Feature("android.hardware.bluetooth", true),
112 new Feature("android.hardware.location", true),
113 new Feature("android.hardware.location.gps", true),
114 new Feature("android.hardware.location.network", true),
115 new Feature("android.hardware.microphone", true),
116 new Feature("android.hardware.sensor.accelerometer", true),
117 new Feature("android.hardware.sensor.compass", true),
118 new Feature("android.hardware.touchscreen", true),
119 new Feature("android.hardware.touchscreen.multitouch", false),
120 new Feature("android.hardware.touchscreen.multitouch.distinct", false),
121 new Feature("android.hardware.wifi", false),
Dan Morrill5df275b2010-08-11 12:19:19 -0700122 };
123
Reena Leef8451ed2011-01-27 17:34:17 -0800124 public static final Feature[] ALL_GINGERBREAD_FEATURES = {
Brian Muramatsud8d37a42011-07-07 14:11:26 -0700125 // Required features in prior releases that became optional in GB
126 new Feature("android.hardware.bluetooth", false),
127 new Feature("android.hardware.camera", false),
128 new Feature("android.hardware.location.gps", false),
129 new Feature("android.hardware.microphone", false),
130 new Feature("android.hardware.sensor.accelerometer", false),
131 new Feature("android.hardware.sensor.compass", false),
132
133 // New features in GB
Reena Leef8451ed2011-01-27 17:34:17 -0800134 new Feature("android.hardware.audio.low_latency", false),
135 new Feature("android.hardware.camera.front", false),
136 new Feature("android.hardware.nfc", false),
137 new Feature("android.hardware.sensor.barometer", false),
138 new Feature("android.hardware.sensor.gyroscope", false),
139 new Feature("android.hardware.touchscreen.multitouch.jazzhand", false),
140 new Feature("android.software.sip", false),
141 new Feature("android.software.sip.voip", false),
142 };
143
Brian Muramatsufc133822011-07-27 11:16:29 -0700144 public static final Feature[] ALL_GINGERBREAD_MR1_FEATURES = {
145 new Feature("android.hardware.usb.accessory", false),
146 };
147
Mac_Wang2e9193f2011-03-10 14:06:58 +0800148 public static final Feature[] ALL_HONEYCOMB_FEATURES = {
Brian Muramatsu30e241c2011-08-01 16:07:47 -0700149 // Required features in prior releases that became optional in HC
150 new Feature("android.hardware.touchscreen", false),
151
Mac_Wang2e9193f2011-03-10 14:06:58 +0800152 new Feature("android.hardware.faketouch", true),
153 };
154
Brian Muramatsu34317ff2011-06-06 14:22:49 -0700155 public static final Feature[] ALL_HONEYCOMB_MR1_FEATURES = {
Brian Muramatsu677fe342011-07-15 10:46:28 -0700156 new Feature("android.hardware.usb.host", false),
Kent Cheng5c0b5d82013-08-07 18:13:26 +0800157 new Feature("android.hardware.usb.accessory", false),
Brian Muramatsu34317ff2011-06-06 14:22:49 -0700158 };
159
Brian Muramatsu177bac52011-07-12 18:28:18 -0700160 public static final Feature[] ALL_HONEYCOMB_MR2_FEATURES = {
161 new Feature("android.hardware.faketouch.multitouch.distinct", false),
162 new Feature("android.hardware.faketouch.multitouch.jazzhand", false),
163 new Feature("android.hardware.screen.landscape", false),
164 new Feature("android.hardware.screen.portrait", false),
165 };
166
Brian Muramatsu278962c2012-01-19 17:37:38 -0800167 public static final Feature[] ALL_ICE_CREAM_SANDWICH_FEATURES = {
168 new Feature(PackageManager.FEATURE_WIFI_DIRECT, false),
169 };
170
Leo Liao0801e932013-01-30 10:23:14 +0800171 public static final Feature[] ALL_JELLY_BEAN_FEATURES = {
172 new Feature(PackageManager.FEATURE_TELEVISION, false),
173 };
174
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700175 public static final Feature[] ALL_JELLY_BEAN_MR2_FEATURES = {
176 new Feature("android.software.app_widgets", false),
Guru Nagarajand8716c92013-07-26 15:55:54 -0700177 new Feature("android.software.input_methods", false),
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700178 new Feature("android.software.home_screen", false),
179 new Feature("android.hardware.bluetooth_le", false),
Guru Nagarajand8716c92013-07-26 15:55:54 -0700180 new Feature("android.hardware.camera.any", false),
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700181 };
182
Martijn Coenen9f342d32013-10-25 13:59:50 -0700183 public static final Feature[] ALL_KITKAT_FEATURES = {
184 new Feature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, false),
Madan Ankapurad58c0292013-11-03 23:42:34 -0800185 new Feature(PackageManager.FEATURE_CONSUMER_IR, false),
186 new Feature(PackageManager.FEATURE_DEVICE_ADMIN, false),
187 new Feature(PackageManager.FEATURE_SENSOR_STEP_COUNTER, false),
188 new Feature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR, false),
Martijn Coenen9f342d32013-10-25 13:59:50 -0700189 };
190
Vinod Krishnan20d67252014-04-30 11:12:01 -0700191 public static final Feature[] ALL_KITKAT_WATCH_FEATURES = {
192 new Feature(PackageManager.FEATURE_SENSOR_HEART_RATE, false),
193 };
194
Vinod Krishnan9a277c82014-08-19 14:17:39 -0700195 public static final Feature[] ALL_LMP_FEATURES = {
196 new Feature(PackageManager.FEATURE_SENSOR_HEART_RATE_ECG, false),
197 };
198
Dan Morrill5df275b2010-08-11 12:19:19 -0700199 @Override
200 public void onCreate(Bundle savedInstanceState) {
201 super.onCreate(savedInstanceState);
202 setContentView(R.layout.fs_main);
Brian Muramatsude694442011-03-09 14:13:01 -0800203 setPassFailButtonClickListeners();
Dan Morrill71351d82010-10-20 15:26:00 -0700204 setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
Dan Morrill5df275b2010-08-11 12:19:19 -0700205
Dan Morrill71351d82010-10-20 15:26:00 -0700206 // some values used to detect warn-able conditions involving multiple
207 // features
Dan Morrill5df275b2010-08-11 12:19:19 -0700208 boolean hasWifi = false;
209 boolean hasTelephony = false;
210 boolean hasIllegalFeature = false;
211
Dan Morrill71351d82010-10-20 15:26:00 -0700212 // get list of all features device thinks it has, & store in a HashMap
213 // for fast lookups
Dan Morrill5df275b2010-08-11 12:19:19 -0700214 HashMap<String, String> actualFeatures = new HashMap<String, String>();
215 for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
216 actualFeatures.put(fi.name, fi.name);
217 }
218
219 // data structure that the SimpleAdapter will use to populate ListView
220 ArrayList<HashMap<String, Object>> listViewData = new ArrayList<HashMap<String, Object>>();
221
222 // roll over all known features & check whether device reports them
223 boolean present = false;
224 int statusIcon;
Brian Muramatsud8d37a42011-07-07 14:11:26 -0700225 Set<Feature> features = new LinkedHashSet<Feature>();
226
227 // add features from latest to last so that the latest requirements are put in the set first
Dan Morrill71351d82010-10-20 15:26:00 -0700228 int apiVersion = Build.VERSION.SDK_INT;
Vinod Krishnan9a277c82014-08-19 14:17:39 -0700229 if (apiVersion >= Build.VERSION_CODES.L) {
230 Collections.addAll(features, ALL_LMP_FEATURES);
231 }
Vinod Krishnan20d67252014-04-30 11:12:01 -0700232 if (apiVersion >= Build.VERSION_CODES.KITKAT_WATCH) {
233 Collections.addAll(features, ALL_KITKAT_WATCH_FEATURES);
234 }
Martijn Coenen9f342d32013-10-25 13:59:50 -0700235 if (apiVersion >= Build.VERSION_CODES.KITKAT) {
236 Collections.addAll(features, ALL_KITKAT_FEATURES);
237 }
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700238 if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Guru Nagarajan89cd9392013-07-26 15:31:11 -0700239 Collections.addAll(features, ALL_JELLY_BEAN_MR2_FEATURES);
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700240 }
Leo Liao0801e932013-01-30 10:23:14 +0800241 if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN) {
242 Collections.addAll(features, ALL_JELLY_BEAN_FEATURES);
243 }
Brian Muramatsu278962c2012-01-19 17:37:38 -0800244 if (apiVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
245 Collections.addAll(features, ALL_ICE_CREAM_SANDWICH_FEATURES);
246 }
Brian Muramatsu177bac52011-07-12 18:28:18 -0700247 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR2) {
248 Collections.addAll(features, ALL_HONEYCOMB_MR2_FEATURES);
249 }
Brian Muramatsu23c84d92011-07-12 17:39:28 -0700250 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR1) {
251 Collections.addAll(features, ALL_HONEYCOMB_MR1_FEATURES);
Reena Leef8451ed2011-01-27 17:34:17 -0800252 }
Mac_Wang2e9193f2011-03-10 14:06:58 +0800253 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB) {
254 Collections.addAll(features, ALL_HONEYCOMB_FEATURES);
255 }
Brian Muramatsufc133822011-07-27 11:16:29 -0700256 if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
257 Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
258 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700259 if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
260 Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
261 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700262 if (apiVersion >= Build.VERSION_CODES.FROYO) {
263 Collections.addAll(features, ALL_FROYO_FEATURES);
Dan Morrill71351d82010-10-20 15:26:00 -0700264 }
Brian Muramatsud8d37a42011-07-07 14:11:26 -0700265 if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
266 Collections.addAll(features, ALL_ECLAIR_FEATURES);
Brian Muramatsu34317ff2011-06-06 14:22:49 -0700267 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700268 for (Feature f : features) {
269 HashMap<String, Object> row = new HashMap<String, Object>();
270 listViewData.add(row);
271 present = actualFeatures.containsKey(f.name);
272 if (present) {
273 // device reports it -- yay! set the happy icon
274 hasWifi = hasWifi || PackageManager.FEATURE_WIFI.equals(f.name);
275 hasTelephony = hasTelephony || PackageManager.FEATURE_TELEPHONY.equals(f.name);
276 statusIcon = R.drawable.fs_good;
277 actualFeatures.remove(f.name);
278 } else if (!present && f.required) {
Dan Morrill71351d82010-10-20 15:26:00 -0700279 // it's required, but device doesn't report it. Boo, set the
280 // bogus icon
Dan Morrill5df275b2010-08-11 12:19:19 -0700281 statusIcon = R.drawable.fs_error;
282 } else {
Dan Morrill71351d82010-10-20 15:26:00 -0700283 // device doesn't report it, but it's not req'd, so can't tell
284 // if there's a problem
Dan Morrill5df275b2010-08-11 12:19:19 -0700285 statusIcon = R.drawable.fs_indeterminate;
286 }
287 row.put("feature", f.name);
288 row.put("icon", statusIcon);
289 }
290
291 // now roll over any remaining features (which are non-standard)
292 for (String feature : actualFeatures.keySet()) {
293 if (feature == null || "".equals(feature))
294 continue;
295 HashMap<String, Object> row = new HashMap<String, Object>();
296 listViewData.add(row);
297 row.put("feature", feature);
298 if (feature.startsWith("android")) { // intentionally not "android."
Dan Morrill71351d82010-10-20 15:26:00 -0700299 // sorry, you're not allowed to squat in the official namespace;
300 // set bogus icon
Dan Morrill5df275b2010-08-11 12:19:19 -0700301 row.put("icon", R.drawable.fs_error);
302 hasIllegalFeature = true;
303 } else {
304 // non-standard features are okay, but flag them just in case
305 row.put("icon", R.drawable.fs_warning);
306 }
307 }
308
Dan Morrill71351d82010-10-20 15:26:00 -0700309 // sort the ListView's data to group by icon type, for easier reading by
310 // humans
Dan Morrill5df275b2010-08-11 12:19:19 -0700311 final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
312 idMap.put(R.drawable.fs_error, 0);
313 idMap.put(R.drawable.fs_warning, 1);
314 idMap.put(R.drawable.fs_indeterminate, 2);
315 idMap.put(R.drawable.fs_good, 3);
316 Collections.sort(listViewData, new Comparator<HashMap<String, Object>>() {
317 public int compare(HashMap<String, Object> left, HashMap<String, Object> right) {
Brian Muramatsue1181872010-08-20 12:59:39 -0700318 int leftId = idMap.get(left.get("icon"));
319 int rightId = idMap.get(right.get("icon"));
Dan Morrill5df275b2010-08-11 12:19:19 -0700320 if (leftId == rightId) {
321 return ((String) left.get("feature")).compareTo((String) right.get("feature"));
322 }
323 if (leftId < rightId)
324 return -1;
325 return 1;
326 }
327 });
328
329 // Set up the SimpleAdapter used to populate the ListView
330 SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
Dan Morrill71351d82010-10-20 15:26:00 -0700331 new String[] {
332 "feature", "icon"
333 }, new int[] {
334 R.id.fs_feature, R.id.fs_icon
335 });
Dan Morrill5df275b2010-08-11 12:19:19 -0700336 adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
337 public boolean setViewValue(View view, Object data, String repr) {
338 try {
339 if (view instanceof ImageView) {
340 ((ImageView) view).setImageResource((Integer) data);
341 } else if (view instanceof TextView) {
342 ((TextView) view).setText((String) data);
343 } else {
344 return false;
345 }
346 return true;
347 } catch (ClassCastException e) {
348 return false;
349 }
350 }
351 });
352 setListAdapter(adapter);
353
Dan Morrill71351d82010-10-20 15:26:00 -0700354 // finally, check for our second-order error cases and set warning text
355 // if necessary
Dan Morrill5df275b2010-08-11 12:19:19 -0700356 StringBuffer sb = new StringBuffer();
357 if (hasIllegalFeature) {
358 sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
359 }
360 if (!hasWifi && !hasTelephony) {
361 sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
362 }
Dan Morrill71351d82010-10-20 15:26:00 -0700363 String warnings = sb.toString().trim();
364 if (warnings == null || "".equals(warnings)) {
365 ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
366 } else {
367 ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
368 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700369 }
370}