blob: 581121e4203c31523a1d14649ee8575f3d631c6f [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
Dan Morrill5df275b2010-08-11 12:19:19 -0700195 @Override
196 public void onCreate(Bundle savedInstanceState) {
197 super.onCreate(savedInstanceState);
198 setContentView(R.layout.fs_main);
Brian Muramatsude694442011-03-09 14:13:01 -0800199 setPassFailButtonClickListeners();
Dan Morrill71351d82010-10-20 15:26:00 -0700200 setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
Dan Morrill5df275b2010-08-11 12:19:19 -0700201
Dan Morrill71351d82010-10-20 15:26:00 -0700202 // some values used to detect warn-able conditions involving multiple
203 // features
Dan Morrill5df275b2010-08-11 12:19:19 -0700204 boolean hasWifi = false;
205 boolean hasTelephony = false;
206 boolean hasIllegalFeature = false;
207
Dan Morrill71351d82010-10-20 15:26:00 -0700208 // get list of all features device thinks it has, & store in a HashMap
209 // for fast lookups
Dan Morrill5df275b2010-08-11 12:19:19 -0700210 HashMap<String, String> actualFeatures = new HashMap<String, String>();
211 for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
212 actualFeatures.put(fi.name, fi.name);
213 }
214
215 // data structure that the SimpleAdapter will use to populate ListView
216 ArrayList<HashMap<String, Object>> listViewData = new ArrayList<HashMap<String, Object>>();
217
218 // roll over all known features & check whether device reports them
219 boolean present = false;
220 int statusIcon;
Brian Muramatsud8d37a42011-07-07 14:11:26 -0700221 Set<Feature> features = new LinkedHashSet<Feature>();
222
223 // add features from latest to last so that the latest requirements are put in the set first
Dan Morrill71351d82010-10-20 15:26:00 -0700224 int apiVersion = Build.VERSION.SDK_INT;
Vinod Krishnan20d67252014-04-30 11:12:01 -0700225 if (apiVersion >= Build.VERSION_CODES.KITKAT_WATCH) {
226 Collections.addAll(features, ALL_KITKAT_WATCH_FEATURES);
227 }
Martijn Coenen9f342d32013-10-25 13:59:50 -0700228 if (apiVersion >= Build.VERSION_CODES.KITKAT) {
229 Collections.addAll(features, ALL_KITKAT_FEATURES);
230 }
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700231 if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Guru Nagarajan89cd9392013-07-26 15:31:11 -0700232 Collections.addAll(features, ALL_JELLY_BEAN_MR2_FEATURES);
Guru Nagarajandcaa4d52013-07-22 13:48:42 -0700233 }
Leo Liao0801e932013-01-30 10:23:14 +0800234 if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN) {
235 Collections.addAll(features, ALL_JELLY_BEAN_FEATURES);
236 }
Brian Muramatsu278962c2012-01-19 17:37:38 -0800237 if (apiVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
238 Collections.addAll(features, ALL_ICE_CREAM_SANDWICH_FEATURES);
239 }
Brian Muramatsu177bac52011-07-12 18:28:18 -0700240 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR2) {
241 Collections.addAll(features, ALL_HONEYCOMB_MR2_FEATURES);
242 }
Brian Muramatsu23c84d92011-07-12 17:39:28 -0700243 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR1) {
244 Collections.addAll(features, ALL_HONEYCOMB_MR1_FEATURES);
Reena Leef8451ed2011-01-27 17:34:17 -0800245 }
Mac_Wang2e9193f2011-03-10 14:06:58 +0800246 if (apiVersion >= Build.VERSION_CODES.HONEYCOMB) {
247 Collections.addAll(features, ALL_HONEYCOMB_FEATURES);
248 }
Brian Muramatsufc133822011-07-27 11:16:29 -0700249 if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
250 Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
251 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700252 if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
253 Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
254 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700255 if (apiVersion >= Build.VERSION_CODES.FROYO) {
256 Collections.addAll(features, ALL_FROYO_FEATURES);
Dan Morrill71351d82010-10-20 15:26:00 -0700257 }
Brian Muramatsud8d37a42011-07-07 14:11:26 -0700258 if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
259 Collections.addAll(features, ALL_ECLAIR_FEATURES);
Brian Muramatsu34317ff2011-06-06 14:22:49 -0700260 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700261 for (Feature f : features) {
262 HashMap<String, Object> row = new HashMap<String, Object>();
263 listViewData.add(row);
264 present = actualFeatures.containsKey(f.name);
265 if (present) {
266 // device reports it -- yay! set the happy icon
267 hasWifi = hasWifi || PackageManager.FEATURE_WIFI.equals(f.name);
268 hasTelephony = hasTelephony || PackageManager.FEATURE_TELEPHONY.equals(f.name);
269 statusIcon = R.drawable.fs_good;
270 actualFeatures.remove(f.name);
271 } else if (!present && f.required) {
Dan Morrill71351d82010-10-20 15:26:00 -0700272 // it's required, but device doesn't report it. Boo, set the
273 // bogus icon
Dan Morrill5df275b2010-08-11 12:19:19 -0700274 statusIcon = R.drawable.fs_error;
275 } else {
Dan Morrill71351d82010-10-20 15:26:00 -0700276 // device doesn't report it, but it's not req'd, so can't tell
277 // if there's a problem
Dan Morrill5df275b2010-08-11 12:19:19 -0700278 statusIcon = R.drawable.fs_indeterminate;
279 }
280 row.put("feature", f.name);
281 row.put("icon", statusIcon);
282 }
283
284 // now roll over any remaining features (which are non-standard)
285 for (String feature : actualFeatures.keySet()) {
286 if (feature == null || "".equals(feature))
287 continue;
288 HashMap<String, Object> row = new HashMap<String, Object>();
289 listViewData.add(row);
290 row.put("feature", feature);
291 if (feature.startsWith("android")) { // intentionally not "android."
Dan Morrill71351d82010-10-20 15:26:00 -0700292 // sorry, you're not allowed to squat in the official namespace;
293 // set bogus icon
Dan Morrill5df275b2010-08-11 12:19:19 -0700294 row.put("icon", R.drawable.fs_error);
295 hasIllegalFeature = true;
296 } else {
297 // non-standard features are okay, but flag them just in case
298 row.put("icon", R.drawable.fs_warning);
299 }
300 }
301
Dan Morrill71351d82010-10-20 15:26:00 -0700302 // sort the ListView's data to group by icon type, for easier reading by
303 // humans
Dan Morrill5df275b2010-08-11 12:19:19 -0700304 final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
305 idMap.put(R.drawable.fs_error, 0);
306 idMap.put(R.drawable.fs_warning, 1);
307 idMap.put(R.drawable.fs_indeterminate, 2);
308 idMap.put(R.drawable.fs_good, 3);
309 Collections.sort(listViewData, new Comparator<HashMap<String, Object>>() {
310 public int compare(HashMap<String, Object> left, HashMap<String, Object> right) {
Brian Muramatsue1181872010-08-20 12:59:39 -0700311 int leftId = idMap.get(left.get("icon"));
312 int rightId = idMap.get(right.get("icon"));
Dan Morrill5df275b2010-08-11 12:19:19 -0700313 if (leftId == rightId) {
314 return ((String) left.get("feature")).compareTo((String) right.get("feature"));
315 }
316 if (leftId < rightId)
317 return -1;
318 return 1;
319 }
320 });
321
322 // Set up the SimpleAdapter used to populate the ListView
323 SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
Dan Morrill71351d82010-10-20 15:26:00 -0700324 new String[] {
325 "feature", "icon"
326 }, new int[] {
327 R.id.fs_feature, R.id.fs_icon
328 });
Dan Morrill5df275b2010-08-11 12:19:19 -0700329 adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
330 public boolean setViewValue(View view, Object data, String repr) {
331 try {
332 if (view instanceof ImageView) {
333 ((ImageView) view).setImageResource((Integer) data);
334 } else if (view instanceof TextView) {
335 ((TextView) view).setText((String) data);
336 } else {
337 return false;
338 }
339 return true;
340 } catch (ClassCastException e) {
341 return false;
342 }
343 }
344 });
345 setListAdapter(adapter);
346
Dan Morrill71351d82010-10-20 15:26:00 -0700347 // finally, check for our second-order error cases and set warning text
348 // if necessary
Dan Morrill5df275b2010-08-11 12:19:19 -0700349 StringBuffer sb = new StringBuffer();
350 if (hasIllegalFeature) {
351 sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
352 }
353 if (!hasWifi && !hasTelephony) {
354 sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
355 }
Dan Morrill71351d82010-10-20 15:26:00 -0700356 String warnings = sb.toString().trim();
357 if (warnings == null || "".equals(warnings)) {
358 ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
359 } else {
360 ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
361 }
Dan Morrill5df275b2010-08-11 12:19:19 -0700362 }
363}