blob: 44f2f28d641694630a0d83709f137ab1bfa922a8 [file] [log] [blame]
/*
* Copyright (C) 2019 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.qstiles;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// This class is based on the same class from notifications
public abstract class InteractiveVerifierActivity extends PassFailButtons.Activity
implements Runnable {
private static final String TAG = "InteractiveVerifier";
private static final String STATE = "state";
private static final String STATUS = "status";
protected static final int SETUP = 0;
protected static final int READY = 1;
protected static final int RETEST = 2;
protected static final int PASS = 3;
protected static final int FAIL = 4;
protected static final int WAIT_FOR_USER = 5;
protected static final int RETEST_AFTER_LONG_DELAY = 6;
protected static final int READY_AFTER_LONG_DELAY = 7;
protected InteractiveTestCase mCurrentTest;
protected PackageManager mPackageManager;
protected Context mContext;
protected Runnable mRunner;
protected View mHandler;
private LayoutInflater mInflater;
private ViewGroup mItemList;
private List<InteractiveTestCase> mTestList;
private Iterator<InteractiveTestCase> mTestOrder;
protected boolean setTileState(boolean enabled) {
int state = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
mPackageManager.setComponentEnabledSetting(
getTileComponentName(), state, PackageManager.DONT_KILL_APP);
return mPackageManager.getComponentEnabledSetting(getTileComponentName()) == state;
}
protected abstract ComponentName getTileComponentName();
protected abstract class InteractiveTestCase {
protected boolean mUserVerified;
protected int status;
private View view;
protected long delayTime = 3000;
protected abstract View inflate(ViewGroup parent);
View getView(ViewGroup parent) {
if (view == null) {
view = inflate(parent);
}
View requestAction = view.requireViewById(R.id.tiles_action_request);
requestAction.setVisibility(showRequestAction() ? View.VISIBLE : View.GONE);
return view;
}
/** @return true if the test should re-run when the test activity starts. */
boolean autoStart() {
return false;
}
/** Set status to {@link #READY} to proceed, or {@link #SETUP} to try again. */
protected void setUp() {
status = READY;
next();
}
/** Set status to {@link #PASS} or @{link #FAIL} to proceed, or {@link #READY} to retry. */
protected void test() {
status = FAIL;
next();
}
/** Do not modify status. */
protected void tearDown() {
next();
}
protected void setFailed() {
status = FAIL;
logFail();
}
protected void setFailed(String message) {
status = FAIL;
logFail(message);
}
protected void logFail() {
logFail(null);
}
protected void logFail(String message) {
logWithStack("failed " + this.getClass().getSimpleName() +
((message == null) ? "" : ": " + message));
}
protected void logFail(String message, Throwable e) {
Log.e(TAG, "failed " + this.getClass().getSimpleName() +
((message == null) ? "" : ": " + message), e);
}
protected boolean showRequestAction() {
return false;
}
protected void requestAction() {
}
}
protected abstract int getTitleResource();
protected abstract int getInstructionsResource();
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
int savedStateIndex = (savedState == null) ? 0 : savedState.getInt(STATE, 0);
int savedStatus = (savedState == null) ? SETUP : savedState.getInt(STATUS, SETUP);
Log.i(TAG, "restored state(" + savedStateIndex + "}, status(" + savedStatus + ")");
mContext = this;
mRunner = this;
mPackageManager = getPackageManager();
mInflater = getLayoutInflater();
View view = mInflater.inflate(R.layout.tiles_main, null);
mItemList = (ViewGroup) view.findViewById(R.id.tiles_test_items);
mHandler = mItemList;
mTestList = new ArrayList<>();
mTestList.addAll(createTestItems());
for (InteractiveTestCase test : mTestList) {
mItemList.addView(test.getView(mItemList));
}
mTestOrder = mTestList.iterator();
for (int i = 0; i < savedStateIndex; i++) {
mCurrentTest = mTestOrder.next();
mCurrentTest.status = PASS;
}
mCurrentTest = mTestOrder.next();
mCurrentTest.status = savedStatus;
setContentView(view);
setPassFailButtonClickListeners();
getPassButton().setEnabled(false);
setInfoResources(getTitleResource(), getInstructionsResource(), -1);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
final int stateIndex = mTestList.indexOf(mCurrentTest);
outState.putInt(STATE, stateIndex);
final int status = mCurrentTest == null ? SETUP : mCurrentTest.status;
outState.putInt(STATUS, status);
Log.i(TAG, "saved state(" + stateIndex + "}, status(" + status + ")");
}
@Override
protected void onResume() {
super.onResume();
//To avoid NPE during onResume,before start to iterate next test order
if (mCurrentTest != null && mCurrentTest.autoStart()) {
mCurrentTest.status = READY;
}
// Makes sure that the tile is there on resume
setTileState(true);
next();
}
@Override
protected void onPause() {
super.onPause();
// Makes sure that the tile is removed when test is not running
setTileState(false);
}
// Interface Utilities
protected void markItem(InteractiveTestCase test) {
if (test == null) {
return;
}
View item = test.view;
ImageView status = (ImageView) item.findViewById(R.id.tiles_status);
switch (test.status) {
case WAIT_FOR_USER:
status.setImageResource(R.drawable.fs_warning);
break;
case SETUP:
case READY:
case RETEST:
status.setImageResource(R.drawable.fs_clock);
setButtonsState(item, true);
break;
case FAIL:
status.setImageResource(R.drawable.fs_error);
setButtonsState(item, false);
break;
case PASS:
status.setImageResource(R.drawable.fs_good);
setButtonsState(item, false);
break;
}
status.invalidate();
}
private void setButtonsState(View parent, boolean enabledAndClickable) {
View buttonPass = parent.findViewById(R.id.tiles_action_pass);
View buttonFail = parent.findViewById(R.id.tiles_action_fail);
View buttonRequest = parent.findViewById(R.id.tiles_action_request);
buttonPass.setEnabled(enabledAndClickable);
buttonPass.setClickable(enabledAndClickable);
buttonFail.setEnabled(enabledAndClickable);
buttonFail.setClickable(enabledAndClickable);
buttonRequest.setEnabled(enabledAndClickable);
buttonRequest.setClickable(enabledAndClickable);
}
protected View createUserPassFail(ViewGroup parent, int messageId,
Object... messageFormatArgs) {
View item = mInflater.inflate(R.layout.tiles_item, parent, false);
TextView instructions = (TextView) item.findViewById(R.id.tiles_instructions);
instructions.setText(getString(messageId, messageFormatArgs));
return item;
}
protected View createAutoItem(ViewGroup parent, int stringId) {
View item = mInflater.inflate(R.layout.tiles_item, parent, false);
TextView instructions = (TextView) item.findViewById(R.id.tiles_instructions);
instructions.setText(stringId);
View buttonPass = item.findViewById(R.id.tiles_action_pass);
View buttonFail = item.findViewById(R.id.tiles_action_fail);
buttonPass.setVisibility(View.GONE);
buttonFail.setVisibility(View.GONE);
return item;
}
// Test management
abstract protected List<InteractiveTestCase> createTestItems();
public void run() {
if (mCurrentTest == null) {
return;
}
markItem(mCurrentTest);
switch (mCurrentTest.status) {
case SETUP:
Log.i(TAG, "running setup for: " + mCurrentTest.getClass().getSimpleName());
mCurrentTest.setUp();
if (mCurrentTest.status == READY_AFTER_LONG_DELAY) {
delay(mCurrentTest.delayTime);
} else {
delay();
}
break;
case WAIT_FOR_USER:
Log.i(TAG, "waiting for user: " + mCurrentTest.getClass().getSimpleName());
break;
case READY_AFTER_LONG_DELAY:
case RETEST_AFTER_LONG_DELAY:
case READY:
case RETEST:
Log.i(TAG, "running test for: " + mCurrentTest.getClass().getSimpleName());
try {
mCurrentTest.test();
if (mCurrentTest.status == RETEST_AFTER_LONG_DELAY) {
delay(mCurrentTest.delayTime);
} else {
delay();
}
} catch (Throwable t) {
mCurrentTest.status = FAIL;
markItem(mCurrentTest);
Log.e(TAG, "FAIL: " + mCurrentTest.getClass().getSimpleName(), t);
mCurrentTest.tearDown();
mCurrentTest = null;
delay();
}
break;
case FAIL:
Log.i(TAG, "FAIL: " + mCurrentTest.getClass().getSimpleName());
mCurrentTest.tearDown();
mCurrentTest = null;
delay();
break;
case PASS:
Log.i(TAG, "pass for: " + mCurrentTest.getClass().getSimpleName());
mCurrentTest.tearDown();
if (mTestOrder.hasNext()) {
mCurrentTest = mTestOrder.next();
Log.i(TAG, "next test is: " + mCurrentTest.getClass().getSimpleName());
next();
} else {
Log.i(TAG, "no more tests");
mCurrentTest = null;
getPassButton().setEnabled(true);
}
break;
}
markItem(mCurrentTest);
}
/**
* Return to the state machine to progress through the tests.
*/
protected void next() {
mHandler.removeCallbacks(mRunner);
mHandler.post(mRunner);
}
/**
* Wait for things to settle before returning to the state machine.
*/
protected void delay() {
delay(3000);
}
protected void sleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Wait for some time.
*/
protected void delay(long waitTime) {
mHandler.removeCallbacks(mRunner);
mHandler.postDelayed(mRunner, waitTime);
}
// UI callbacks
public void actionPressed(View v) {
if (mCurrentTest != null) {
int id = v.getId();
if (id == R.id.tiles_action_pass) {
mCurrentTest.status = PASS;
mCurrentTest.mUserVerified = true;
next();
} else if (id == R.id.tiles_action_fail) {
mCurrentTest.status = FAIL;
mCurrentTest.mUserVerified = true;
next();
} else if (id == R.id.tiles_action_request) {
mCurrentTest.status = WAIT_FOR_USER;
v.setEnabled(false);
mHandler.post(mCurrentTest::requestAction);
next();
}
}
}
// Utilities
protected void logWithStack(String message) {
Throwable stackTrace = new Throwable();
stackTrace.fillInStackTrace();
Log.e(TAG, message, stackTrace);
}
protected void setPassFailButtonsEnabledState(boolean enabled) {
View currentView = mCurrentTest.view;
currentView.requireViewById(R.id.tiles_action_pass).setEnabled(enabled);
currentView.requireViewById(R.id.tiles_action_fail).setEnabled(enabled);
}
}