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 package com.android.cts.verifier;
18 
19 import android.app.AlertDialog;
20 import android.app.ListActivity;
21 import android.content.DialogInterface;
22 import android.content.SharedPreferences;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.PermissionInfo;
27 import android.os.Bundle;
28 import android.util.Log;
29 import android.view.Menu;
30 import android.view.MenuInflater;
31 import android.view.MenuItem;
32 import android.view.View;
33 import android.view.Window;
34 import android.widget.CompoundButton;
35 import android.widget.Switch;
36 import android.widget.Toast;
37 
38 /** Top-level {@link ListActivity} for launching tests and managing results. */
39 public class TestListActivity extends AbstractTestListActivity implements View.OnClickListener {
40     private static final int CTS_VERIFIER_PERMISSION_REQUEST = 1;
41 
42     private static final String TAG = TestListActivity.class.getSimpleName();
43     // Records the current display mode.
44     // Default is unfolded mode, and it will be changed when clicking the switch button.
45     public static String sCurrentDisplayMode = DisplayMode.UNFOLDED.toString();
46     // Flag of launch app to fetch the unfolded/folded tests in main view from AndroidManifest.xml.
47     protected static boolean sInitialLaunch;
48 
49     // Enumerates the display modes, including unfolded and folded.
50     protected enum DisplayMode {
51         UNFOLDED, FOLDED;
52 
53         @Override
toString()54         public String toString() {
55             return name().toLowerCase();
56         }
57 
58         /**
59          * Coverts the mode as suffix with brackets for test name.
60          *
61          * @return A string containing mode with brackets for folded mode;
62          *         empty string for unfolded mode.
63          */
asSuffix()64         public String asSuffix() {
65             if (name().equals(FOLDED.name())) {
66                 return String.format("[%s]", toString());
67             }
68             return "";
69         }
70     }
71 
72     @Override
onClick(View v)73     public void onClick (View v) {
74         handleMenuItemSelected(v.getId());
75     }
76 
77     @Override
onCreate(Bundle savedInstanceState)78     protected void onCreate(Bundle savedInstanceState) {
79         super.onCreate(savedInstanceState);
80 
81         try {
82             PackageManager pm = getPackageManager();
83             PackageInfo packageInfo = pm.getPackageInfo(
84                     getApplicationInfo().packageName, PackageManager.GET_PERMISSIONS);
85 
86             if (packageInfo.requestedPermissions != null) {
87                 for (String permission : packageInfo.requestedPermissions) {
88                     Log.v(TAG, "Checking permissions for: " + permission);
89                     try {
90                         PermissionInfo info = pm.getPermissionInfo(permission, 0);
91                         if ((info.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) == 0) {
92                             continue;
93                         }
94                     } catch (NameNotFoundException e) {
95                         Log.v(TAG, "Checking permissions for: " + permission + "not found");
96                         continue;
97                     }
98                     if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
99                         requestPermissions(packageInfo.requestedPermissions,
100                                 CTS_VERIFIER_PERMISSION_REQUEST);
101                         /* don't return here. Some tests (i.e. USB Restrict Access test)
102                          * which need to run even if permissions are incomplete.
103                          */
104                     }
105                 }
106             }
107             createContinue();
108         } catch (NameNotFoundException e) {
109             Log.e(TAG, "Unable to load package's permissions", e);
110             Toast.makeText(this, R.string.runtime_permissions_error, Toast.LENGTH_SHORT).show();
111         }
112     }
113 
createContinue()114     private void createContinue() {
115         if (!isTaskRoot()) {
116             finish();
117         }
118         sInitialLaunch = true;
119 
120         setTitle(getString(R.string.title_version, Version.getVersionName(this)));
121 
122         if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
123             View footer = getLayoutInflater().inflate(R.layout.test_list_footer, null);
124 
125             footer.findViewById(R.id.clear).setOnClickListener(this);
126             footer.findViewById(R.id.export).setOnClickListener(this);
127 
128             getListView().addFooterView(footer);
129         }
130 
131         setTestListAdapter(new ManifestTestListAdapter(this, null));
132     }
133 
134     @Override
onRequestPermissionsResult( int requestCode, String permissions[], int[] grantResults)135     public void onRequestPermissionsResult(
136             int requestCode, String permissions[], int[] grantResults) {
137         if (requestCode == CTS_VERIFIER_PERMISSION_REQUEST) {
138             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
139                 createContinue();
140                 return;
141             }
142             Log.v(TAG, "Permission not granted.");
143             Toast.makeText(this, R.string.runtime_permissions_error, Toast.LENGTH_SHORT).show();
144         }
145     }
146 
147     @Override
onCreateOptionsMenu(Menu menu)148     public boolean onCreateOptionsMenu(Menu menu) {
149         MenuInflater inflater = getMenuInflater();
150         inflater.inflate(R.menu.test_list_menu, menu);
151 
152         // Switch button for unfolded and folded tests.
153         MenuItem item = (MenuItem) menu.findItem(R.id.switch_item);
154         item.setActionView(R.layout.display_mode_switch);
155         Switch displayModeSwitch = item.getActionView().findViewById(R.id.switch_button);
156 
157         // Restores the original display mode when launching the app after killing the process.
158         // Otherwise, gets the current display mode to show switch status.
159         boolean isFoldedMode;
160         if (sInitialLaunch) {
161             isFoldedMode = getCurrentDisplayMode().equals(DisplayMode.FOLDED.toString());
162         } else {
163             isFoldedMode = sCurrentDisplayMode.equals(DisplayMode.FOLDED.toString());
164         }
165         displayModeSwitch.setChecked(isFoldedMode);
166 
167         displayModeSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
168             @Override
169             public void onCheckedChanged(CompoundButton buttonView,
170                 boolean isChecked) {
171                 if (isChecked) {
172                     sCurrentDisplayMode = DisplayMode.FOLDED.toString();
173                 } else {
174                     sCurrentDisplayMode = DisplayMode.UNFOLDED.toString();
175                 }
176                 handleSwitchItemSelected();
177             }
178         });
179         return true;
180     }
181 
182     @Override
onOptionsItemSelected(MenuItem item)183     public boolean onOptionsItemSelected(MenuItem item) {
184         return handleMenuItemSelected(item.getItemId()) ? true : super.onOptionsItemSelected(item);
185     }
186 
handleClearItemSelected()187     private void handleClearItemSelected() {
188         new AlertDialog.Builder(this)
189             .setMessage(R.string.test_results_clear_title)
190             .setPositiveButton(R.string.test_results_clear_yes,
191                     new DialogInterface.OnClickListener() {
192                        public void onClick(DialogInterface dialog, int id) {
193                             mAdapter.clearTestResults();
194                             Toast.makeText(
195                                 TestListActivity.this,
196                                 R.string.test_results_cleared,
197                                 Toast.LENGTH_SHORT)
198                                     .show();
199                        }
200                    })
201             .setNegativeButton(R.string.test_results_clear_cancel, null)
202             .show();
203     }
204 
handleExportItemSelected()205     private void handleExportItemSelected() {
206         new ReportExporter(this, mAdapter).execute();
207     }
208 
209     // Sets up the flags after switching display mode and reloads tests on UI.
handleSwitchItemSelected()210     private void handleSwitchItemSelected() {
211         setCurrentDisplayMode(sCurrentDisplayMode);
212         mAdapter.loadTestResults();
213     }
214 
handleMenuItemSelected(int id)215     private boolean handleMenuItemSelected(int id) {
216         if (id == R.id.clear) {
217             handleClearItemSelected();
218         } else if (id == R.id.export) {
219             handleExportItemSelected();
220         } else {
221             return false;
222         }
223 
224         return true;
225     }
226 
227     /**
228      * Sets current display mode to sharedpreferences.
229      *
230      * @param mode A string of current display mode.
231      */
setCurrentDisplayMode(String mode)232     private void setCurrentDisplayMode(String mode) {
233         SharedPreferences pref = getSharedPreferences(DisplayMode.class.getName(), MODE_PRIVATE);
234         pref.edit().putString(DisplayMode.class.getName(), mode).commit();
235     }
236 
237     /**
238      * Gets current display mode from sharedpreferences.
239      *
240      * @return A string of current display mode.
241      */
getCurrentDisplayMode()242     private String getCurrentDisplayMode() {
243         String mode = getSharedPreferences(DisplayMode.class.getName(), MODE_PRIVATE)
244             .getString(DisplayMode.class.getName(), "");
245         return mode;
246     }
247 }