| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.verifier; |
| |
| import com.android.compatibility.common.util.ReportLog; |
| |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.ContentObserver; |
| import android.database.Cursor; |
| import android.os.AsyncTask; |
| import android.os.Handler; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.BaseAdapter; |
| import android.widget.ListView; |
| import android.widget.TextView; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * {@link BaseAdapter} that handles loading, refreshing, and setting test |
| * results. What tests are shown can be customized by overriding |
| * {@link #getRows()}. See {@link ArrayTestListAdapter} and |
| * {@link ManifestTestListAdapter} for examples. |
| */ |
| public abstract class TestListAdapter extends BaseAdapter { |
| |
| /** Activities implementing {@link Intent#ACTION_MAIN} and this will appear in the list. */ |
| public static final String CATEGORY_MANUAL_TEST = "android.cts.intent.category.MANUAL_TEST"; |
| |
| /** View type for a category of tests like "Sensors" or "Features" */ |
| private static final int CATEGORY_HEADER_VIEW_TYPE = 0; |
| |
| /** View type for an actual test like the Accelerometer test. */ |
| private static final int TEST_VIEW_TYPE = 1; |
| |
| /** Padding around the text views and icons. */ |
| private static final int PADDING = 10; |
| |
| private final Context mContext; |
| |
| /** Immutable data of tests like the test's title and launch intent. */ |
| private final List<TestListItem> mRows = new ArrayList<TestListItem>(); |
| |
| /** Mutable test results that will change as each test activity finishes. */ |
| private final Map<String, Integer> mTestResults = new HashMap<String, Integer>(); |
| |
| /** Map from test name to test details. */ |
| private final Map<String, String> mTestDetails = new HashMap<String, String>(); |
| |
| /** Map from test name to {@link ReportLog}. */ |
| private final Map<String, ReportLog> mReportLogs = new HashMap<String, ReportLog>(); |
| |
| private final LayoutInflater mLayoutInflater; |
| |
| /** {@link ListView} row that is either a test category header or a test. */ |
| public static class TestListItem { |
| |
| /** Title shown in the {@link ListView}. */ |
| final String title; |
| |
| /** Test name with class and test ID to uniquely identify the test. Null for categories. */ |
| final String testName; |
| |
| /** Intent used to launch the activity from the list. Null for categories. */ |
| final Intent intent; |
| |
| /** Features necessary to run this test. */ |
| final String[] requiredFeatures; |
| |
| /** Features such that, if any present, the test gets excluded from being shown. */ |
| final String[] excludedFeatures; |
| |
| /** If any of of the features are present the test is meaningful to run. */ |
| final String[] applicableFeatures; |
| |
| public static TestListItem newTest(Context context, int titleResId, String testName, |
| Intent intent, String[] requiredFeatures, String[] excludedFeatures, |
| String[] applicableFeatures) { |
| return newTest(context.getString(titleResId), testName, intent, requiredFeatures, |
| excludedFeatures, applicableFeatures); |
| } |
| |
| public static TestListItem newTest(Context context, int titleResId, String testName, |
| Intent intent, String[] requiredFeatures, String[] excludedFeatures) { |
| return newTest(context.getString(titleResId), testName, intent, requiredFeatures, |
| excludedFeatures, null); |
| } |
| |
| public static TestListItem newTest(Context context, int titleResId, String testName, |
| Intent intent, String[] requiredFeatures) { |
| return newTest(context.getString(titleResId), testName, intent, requiredFeatures, null, |
| null); |
| } |
| |
| public static TestListItem newTest(String title, String testName, Intent intent, |
| String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) { |
| return new TestListItem(title, testName, intent, requiredFeatures, excludedFeatures, |
| applicableFeatures); |
| } |
| |
| public static TestListItem newTest(String title, String testName, Intent intent, |
| String[] requiredFeatures, String[] excludedFeatures) { |
| return new TestListItem(title, testName, intent, requiredFeatures, excludedFeatures, |
| null); |
| } |
| |
| public static TestListItem newTest(String title, String testName, Intent intent, |
| String[] requiredFeatures) { |
| return new TestListItem(title, testName, intent, requiredFeatures, null, null); |
| } |
| |
| public static TestListItem newCategory(Context context, int titleResId) { |
| return newCategory(context.getString(titleResId)); |
| } |
| |
| public static TestListItem newCategory(String title) { |
| return new TestListItem(title, null, null, null, null, null); |
| } |
| |
| private TestListItem(String title, String testName, Intent intent, |
| String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) { |
| this.title = title; |
| this.testName = testName; |
| this.intent = intent; |
| this.requiredFeatures = requiredFeatures; |
| this.excludedFeatures = excludedFeatures; |
| this.applicableFeatures = applicableFeatures; |
| } |
| |
| boolean isTest() { |
| return intent != null; |
| } |
| } |
| |
| public TestListAdapter(Context context) { |
| this.mContext = context; |
| this.mLayoutInflater = |
| (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| |
| TestResultContentObserver observer = new TestResultContentObserver(); |
| ContentResolver resolver = context.getContentResolver(); |
| resolver.registerContentObserver(TestResultsProvider.RESULTS_CONTENT_URI, true, observer); |
| } |
| |
| public void loadTestResults() { |
| new RefreshTestResultsTask().execute(); |
| } |
| |
| public void clearTestResults() { |
| new ClearTestResultsTask().execute(); |
| } |
| |
| public void setTestResult(TestResult testResult) { |
| new SetTestResultTask(testResult.getName(), testResult.getResult(), |
| testResult.getDetails(), testResult.getReportLog()).execute(); |
| } |
| |
| class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> { |
| @Override |
| protected RefreshResult doInBackground(Void... params) { |
| List<TestListItem> rows = getRows(); |
| return getRefreshResults(rows); |
| } |
| |
| @Override |
| protected void onPostExecute(RefreshResult result) { |
| super.onPostExecute(result); |
| mRows.clear(); |
| mRows.addAll(result.mItems); |
| mTestResults.clear(); |
| mTestResults.putAll(result.mResults); |
| mTestDetails.clear(); |
| mTestDetails.putAll(result.mDetails); |
| mReportLogs.clear(); |
| mReportLogs.putAll(result.mReportLogs); |
| notifyDataSetChanged(); |
| } |
| } |
| |
| static class RefreshResult { |
| List<TestListItem> mItems; |
| Map<String, Integer> mResults; |
| Map<String, String> mDetails; |
| Map<String, ReportLog> mReportLogs; |
| |
| RefreshResult( |
| List<TestListItem> items, |
| Map<String, Integer> results, |
| Map<String, String> details, |
| Map<String, ReportLog> reportLogs) { |
| mItems = items; |
| mResults = results; |
| mDetails = details; |
| mReportLogs = reportLogs; |
| } |
| } |
| |
| protected abstract List<TestListItem> getRows(); |
| |
| static final String[] REFRESH_PROJECTION = { |
| TestResultsProvider._ID, |
| TestResultsProvider.COLUMN_TEST_NAME, |
| TestResultsProvider.COLUMN_TEST_RESULT, |
| TestResultsProvider.COLUMN_TEST_DETAILS, |
| TestResultsProvider.COLUMN_TEST_METRICS, |
| }; |
| |
| RefreshResult getRefreshResults(List<TestListItem> items) { |
| Map<String, Integer> results = new HashMap<String, Integer>(); |
| Map<String, String> details = new HashMap<String, String>(); |
| Map<String, ReportLog> reportLogs = new HashMap<String, ReportLog>(); |
| ContentResolver resolver = mContext.getContentResolver(); |
| Cursor cursor = null; |
| try { |
| cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI, REFRESH_PROJECTION, |
| null, null, null); |
| if (cursor.moveToFirst()) { |
| do { |
| String testName = cursor.getString(1); |
| int testResult = cursor.getInt(2); |
| String testDetails = cursor.getString(3); |
| ReportLog reportLog = (ReportLog) deserialize(cursor.getBlob(4)); |
| results.put(testName, testResult); |
| details.put(testName, testDetails); |
| reportLogs.put(testName, reportLog); |
| } while (cursor.moveToNext()); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| return new RefreshResult(items, results, details, reportLogs); |
| } |
| |
| class ClearTestResultsTask extends AsyncTask<Void, Void, Void> { |
| |
| @Override |
| protected Void doInBackground(Void... params) { |
| ContentResolver resolver = mContext.getContentResolver(); |
| resolver.delete(TestResultsProvider.RESULTS_CONTENT_URI, "1", null); |
| return null; |
| } |
| } |
| |
| class SetTestResultTask extends AsyncTask<Void, Void, Void> { |
| |
| private final String mTestName; |
| |
| private final int mResult; |
| |
| private final String mDetails; |
| |
| private final ReportLog mReportLog; |
| |
| SetTestResultTask( |
| String testName, |
| int result, |
| String details, |
| ReportLog reportLog) { |
| mTestName = testName; |
| mResult = result; |
| mDetails = details; |
| mReportLog = reportLog; |
| } |
| |
| @Override |
| protected Void doInBackground(Void... params) { |
| TestResultsProvider.setTestResult(mContext, mTestName, mResult, mDetails, mReportLog); |
| return null; |
| } |
| } |
| |
| class TestResultContentObserver extends ContentObserver { |
| |
| public TestResultContentObserver() { |
| super(new Handler()); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange) { |
| super.onChange(selfChange); |
| loadTestResults(); |
| } |
| } |
| |
| @Override |
| public boolean areAllItemsEnabled() { |
| // Section headers for test categories are not clickable. |
| return false; |
| } |
| |
| @Override |
| public boolean isEnabled(int position) { |
| return getItem(position).isTest(); |
| } |
| |
| @Override |
| public int getItemViewType(int position) { |
| return getItem(position).isTest() ? TEST_VIEW_TYPE : CATEGORY_HEADER_VIEW_TYPE; |
| } |
| |
| @Override |
| public int getViewTypeCount() { |
| return 2; |
| } |
| |
| @Override |
| public int getCount() { |
| return mRows.size(); |
| } |
| |
| @Override |
| public TestListItem getItem(int position) { |
| return mRows.get(position); |
| } |
| |
| @Override |
| public long getItemId(int position) { |
| return position; |
| } |
| |
| public int getTestResult(int position) { |
| TestListItem item = getItem(position); |
| return mTestResults.containsKey(item.testName) |
| ? mTestResults.get(item.testName) |
| : TestResult.TEST_RESULT_NOT_EXECUTED; |
| } |
| |
| public String getTestDetails(int position) { |
| TestListItem item = getItem(position); |
| return mTestDetails.containsKey(item.testName) |
| ? mTestDetails.get(item.testName) |
| : null; |
| } |
| |
| public ReportLog getReportLog(int position) { |
| TestListItem item = getItem(position); |
| return mReportLogs.containsKey(item.testName) |
| ? mReportLogs.get(item.testName) |
| : null; |
| } |
| |
| public boolean allTestsPassed() { |
| for (TestListItem item : mRows) { |
| if (item.isTest() && (!mTestResults.containsKey(item.testName) |
| || (mTestResults.get(item.testName) != TestResult.TEST_RESULT_PASSED))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| TextView textView; |
| if (convertView == null) { |
| int layout = getLayout(position); |
| textView = (TextView) mLayoutInflater.inflate(layout, parent, false); |
| } else { |
| textView = (TextView) convertView; |
| } |
| |
| TestListItem item = getItem(position); |
| textView.setText(item.title); |
| textView.setPadding(PADDING, 0, PADDING, 0); |
| textView.setCompoundDrawablePadding(PADDING); |
| |
| if (item.isTest()) { |
| int testResult = getTestResult(position); |
| int backgroundResource = 0; |
| int iconResource = 0; |
| |
| /** TODO: Remove fs_ prefix from feature icons since they are used here too. */ |
| switch (testResult) { |
| case TestResult.TEST_RESULT_PASSED: |
| backgroundResource = R.drawable.test_pass_gradient; |
| iconResource = R.drawable.fs_good; |
| break; |
| |
| case TestResult.TEST_RESULT_FAILED: |
| backgroundResource = R.drawable.test_fail_gradient; |
| iconResource = R.drawable.fs_error; |
| break; |
| |
| case TestResult.TEST_RESULT_NOT_EXECUTED: |
| break; |
| |
| default: |
| throw new IllegalArgumentException("Unknown test result: " + testResult); |
| } |
| |
| textView.setBackgroundResource(backgroundResource); |
| textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0); |
| } |
| |
| return textView; |
| } |
| |
| private int getLayout(int position) { |
| int viewType = getItemViewType(position); |
| switch (viewType) { |
| case CATEGORY_HEADER_VIEW_TYPE: |
| return R.layout.test_category_row; |
| case TEST_VIEW_TYPE: |
| return android.R.layout.simple_list_item_1; |
| default: |
| throw new IllegalArgumentException("Illegal view type: " + viewType); |
| |
| } |
| } |
| |
| private static Object deserialize(byte[] bytes) { |
| if (bytes == null || bytes.length == 0) { |
| return null; |
| } |
| ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); |
| ObjectInputStream objectInput = null; |
| try { |
| objectInput = new ObjectInputStream(byteStream); |
| return objectInput.readObject(); |
| } catch (IOException e) { |
| return null; |
| } catch (ClassNotFoundException e) { |
| return null; |
| } finally { |
| try { |
| if (objectInput != null) { |
| objectInput.close(); |
| } |
| byteStream.close(); |
| } catch (IOException e) { |
| // Ignore close exception. |
| } |
| } |
| } |
| } |