Home | History | Annotate | Download | only in features
      1 /*
      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  */
     24 
     25 package com.android.cts.verifier.features;
     26 
     27 import com.android.cts.verifier.PassFailButtons;
     28 import com.android.cts.verifier.R;
     29 
     30 import android.content.pm.FeatureInfo;
     31 import android.content.pm.PackageManager;
     32 import android.os.Build;
     33 import android.os.Bundle;
     34 import android.view.View;
     35 import android.widget.ImageView;
     36 import android.widget.SimpleAdapter;
     37 import android.widget.TextView;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Collections;
     41 import java.util.Comparator;
     42 import java.util.HashMap;
     43 import java.util.LinkedHashSet;
     44 import java.util.Set;
     45 
     46 public class FeatureSummaryActivity extends PassFailButtons.ListActivity {
     47     /**
     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.
     70          *
     71          * @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         }
     79 
     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         }
     96     }
     97 
     98     public static final Feature[] ALL_ECLAIR_FEATURES = {
     99             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),
    103             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),
    108     };
    109 
    110     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),
    122     };
    123 
    124     public static final Feature[] ALL_GINGERBREAD_FEATURES = {
    125             // 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
    134             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 
    144     public static final Feature[] ALL_GINGERBREAD_MR1_FEATURES = {
    145             new Feature("android.hardware.usb.accessory", false),
    146     };
    147 
    148     public static final Feature[] ALL_HONEYCOMB_FEATURES = {
    149             // Required features in prior releases that became optional in HC
    150             new Feature("android.hardware.touchscreen", false),
    151 
    152             new Feature("android.hardware.faketouch", true),
    153     };
    154 
    155     public static final Feature[] ALL_HONEYCOMB_MR1_FEATURES = {
    156             new Feature("android.hardware.usb.host", false),
    157             new Feature("android.hardware.usb.accessory", true),
    158     };
    159 
    160     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 
    167     public static final Feature[] ALL_ICE_CREAM_SANDWICH_FEATURES = {
    168             new Feature(PackageManager.FEATURE_WIFI_DIRECT, false),
    169     };
    170 
    171     public static final Feature[] ALL_JELLY_BEAN_FEATURES = {
    172             new Feature(PackageManager.FEATURE_TELEVISION, false),
    173     };
    174 
    175     public static final Feature[] ALL_JELLY_BEAN_MR2_FEATURES = {
    176             new Feature("android.software.app_widgets", false),
    177             new Feature("android.software.input_methods", false),
    178             new Feature("android.software.home_screen", false),
    179             new Feature("android.hardware.bluetooth_le", false),
    180             new Feature("android.hardware.camera.any", false),
    181     };
    182 
    183     @Override
    184     public void onCreate(Bundle savedInstanceState) {
    185         super.onCreate(savedInstanceState);
    186         setContentView(R.layout.fs_main);
    187         setPassFailButtonClickListeners();
    188         setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
    189 
    190         // some values used to detect warn-able conditions involving multiple
    191         // features
    192         boolean hasWifi = false;
    193         boolean hasTelephony = false;
    194         boolean hasIllegalFeature = false;
    195 
    196         // get list of all features device thinks it has, & store in a HashMap
    197         // for fast lookups
    198         HashMap<String, String> actualFeatures = new HashMap<String, String>();
    199         for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
    200             actualFeatures.put(fi.name, fi.name);
    201         }
    202 
    203         // data structure that the SimpleAdapter will use to populate ListView
    204         ArrayList<HashMap<String, Object>> listViewData = new ArrayList<HashMap<String, Object>>();
    205 
    206         // roll over all known features & check whether device reports them
    207         boolean present = false;
    208         int statusIcon;
    209         Set<Feature> features = new LinkedHashSet<Feature>();
    210 
    211         // add features from latest to last so that the latest requirements are put in the set first
    212         int apiVersion = Build.VERSION.SDK_INT;
    213         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    214             Collections.addAll(features, ALL_JELLY_BEAN_MR2_FEATURES);
    215         }
    216         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN) {
    217             Collections.addAll(features, ALL_JELLY_BEAN_FEATURES);
    218         }
    219         if (apiVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    220             Collections.addAll(features, ALL_ICE_CREAM_SANDWICH_FEATURES);
    221         }
    222         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR2) {
    223             Collections.addAll(features, ALL_HONEYCOMB_MR2_FEATURES);
    224         }
    225         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR1) {
    226             Collections.addAll(features, ALL_HONEYCOMB_MR1_FEATURES);
    227         }
    228         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB) {
    229             Collections.addAll(features, ALL_HONEYCOMB_FEATURES);
    230         }
    231         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
    232             Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
    233         }
    234         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
    235             Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
    236         }
    237         if (apiVersion >= Build.VERSION_CODES.FROYO) {
    238             Collections.addAll(features, ALL_FROYO_FEATURES);
    239         }
    240         if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
    241             Collections.addAll(features, ALL_ECLAIR_FEATURES);
    242         }
    243         for (Feature f : features) {
    244             HashMap<String, Object> row = new HashMap<String, Object>();
    245             listViewData.add(row);
    246             present = actualFeatures.containsKey(f.name);
    247             if (present) {
    248                 // device reports it -- yay! set the happy icon
    249                 hasWifi = hasWifi || PackageManager.FEATURE_WIFI.equals(f.name);
    250                 hasTelephony = hasTelephony || PackageManager.FEATURE_TELEPHONY.equals(f.name);
    251                 statusIcon = R.drawable.fs_good;
    252                 actualFeatures.remove(f.name);
    253             } else if (!present && f.required) {
    254                 // it's required, but device doesn't report it. Boo, set the
    255                 // bogus icon
    256                 statusIcon = R.drawable.fs_error;
    257             } else {
    258                 // device doesn't report it, but it's not req'd, so can't tell
    259                 // if there's a problem
    260                 statusIcon = R.drawable.fs_indeterminate;
    261             }
    262             row.put("feature", f.name);
    263             row.put("icon", statusIcon);
    264         }
    265 
    266         // now roll over any remaining features (which are non-standard)
    267         for (String feature : actualFeatures.keySet()) {
    268             if (feature == null || "".equals(feature))
    269                 continue;
    270             HashMap<String, Object> row = new HashMap<String, Object>();
    271             listViewData.add(row);
    272             row.put("feature", feature);
    273             if (feature.startsWith("android")) { // intentionally not "android."
    274                 // sorry, you're not allowed to squat in the official namespace;
    275                 // set bogus icon
    276                 row.put("icon", R.drawable.fs_error);
    277                 hasIllegalFeature = true;
    278             } else {
    279                 // non-standard features are okay, but flag them just in case
    280                 row.put("icon", R.drawable.fs_warning);
    281             }
    282         }
    283 
    284         // sort the ListView's data to group by icon type, for easier reading by
    285         // humans
    286         final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
    287         idMap.put(R.drawable.fs_error, 0);
    288         idMap.put(R.drawable.fs_warning, 1);
    289         idMap.put(R.drawable.fs_indeterminate, 2);
    290         idMap.put(R.drawable.fs_good, 3);
    291         Collections.sort(listViewData, new Comparator<HashMap<String, Object>>() {
    292             public int compare(HashMap<String, Object> left, HashMap<String, Object> right) {
    293                 int leftId = idMap.get(left.get("icon"));
    294                 int rightId = idMap.get(right.get("icon"));
    295                 if (leftId == rightId) {
    296                     return ((String) left.get("feature")).compareTo((String) right.get("feature"));
    297                 }
    298                 if (leftId < rightId)
    299                     return -1;
    300                 return 1;
    301             }
    302         });
    303 
    304         // Set up the SimpleAdapter used to populate the ListView
    305         SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
    306                 new String[] {
    307                         "feature", "icon"
    308                 }, new int[] {
    309                         R.id.fs_feature, R.id.fs_icon
    310                 });
    311         adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
    312             public boolean setViewValue(View view, Object data, String repr) {
    313                 try {
    314                     if (view instanceof ImageView) {
    315                         ((ImageView) view).setImageResource((Integer) data);
    316                     } else if (view instanceof TextView) {
    317                         ((TextView) view).setText((String) data);
    318                     } else {
    319                         return false;
    320                     }
    321                     return true;
    322                 } catch (ClassCastException e) {
    323                     return false;
    324                 }
    325             }
    326         });
    327         setListAdapter(adapter);
    328 
    329         // finally, check for our second-order error cases and set warning text
    330         // if necessary
    331         StringBuffer sb = new StringBuffer();
    332         if (hasIllegalFeature) {
    333             sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
    334         }
    335         if (!hasWifi && !hasTelephony) {
    336             sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
    337         }
    338         String warnings = sb.toString().trim();
    339         if (warnings == null || "".equals(warnings)) {
    340             ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
    341         } else {
    342             ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
    343         }
    344     }
    345 }
    346