blob: 9204de304d3379026d4d813b7ab575d4393f12df [file] [log] [blame]
/*
* Copyright (C) 2012 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.camera.intents;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestResult;
import java.util.TreeSet;
/**
* Tests for manual verification of camera intents being fired.
*
* android.hardware.Camera.ACTION_NEW_PICTURE - this should fire
* when a new picture was captured by the camera app, and it has been
* added to the media store.
* android.hardware.Camera.ACTION_NEW_VIDEO - this should fire when a new
* video has been captured by the camera app, and it has been added
* to the media store.
*
* The tests verify this both by asking the user to manually launch
* the camera activity, as well as by programatically launching the camera
* activity via MediaStore intents.
*
* Please ensure when replacing the default camera app on a device,
* that these intents are still firing as a lot of 3rd party applications
* (e.g. social network apps that upload a photo after you take a picture)
* rely on this functionality present and correctly working.
*/
public class CameraIntentsActivity extends PassFailButtons.Activity
implements OnClickListener, SurfaceHolder.Callback {
private static final String TAG = "CameraIntents";
private static final int STATE_OFF = 0;
private static final int STATE_STARTED = 1;
private static final int STATE_SUCCESSFUL = 2;
private static final int STATE_FAILED = 3;
private static final int NUM_STAGES = 4;
private static final String STAGE_INDEX_EXTRA = "stageIndex";
private static final int STAGE_APP_PICTURE = 0;
private static final int STAGE_APP_VIDEO = 1;
private static final int STAGE_INTENT_PICTURE = 2;
private static final int STAGE_INTENT_VIDEO = 3;
private static String[] EXPECTED_INTENTS = new String[] {
Camera.ACTION_NEW_PICTURE,
Camera.ACTION_NEW_VIDEO,
null,
Camera.ACTION_NEW_VIDEO
};
private ImageButton mPassButton;
private ImageButton mFailButton;
private Button mStartTestButton;
private int mState = STATE_OFF;
private BroadcastReceiver mReceiver;
private IntentFilter mFilterPicture;
private boolean mActivityResult = false;
private boolean mDetectCheating = false;
private StringBuilder mReportBuilder = new StringBuilder();
private final TreeSet<String> mTestedCombinations = new TreeSet<String>();
private final TreeSet<String> mUntestedCombinations = new TreeSet<String>();
/* Callback from mReceiver#onReceive */
public void onReceivedIntent(Intent intent) {
Log.v(TAG, "Received intent " + intent.toString());
if (mState == STATE_STARTED) {
/* this can happen if..
the camera apps intent finishes,
user returns to cts verifier,
user leaves cts verifier and tries to fake receiver intents
*/
if (mDetectCheating) {
Log.w(TAG, "Cheating attempt suppressed");
mState = STATE_FAILED;
}
String expectedIntent = EXPECTED_INTENTS[getStageIndex()];
if (expectedIntent != intent.getAction()) {
Log.e(TAG, "FAIL: Test # " + getStageIndex()
+ " must not broadcast "
+ intent.getAction()
+ ", expected: "
+ (expectedIntent != null ? expectedIntent : "no intent"));
mState = STATE_FAILED;
}
if (mState != STATE_FAILED) {
mState = STATE_SUCCESSFUL;
mPassButton.setEnabled(true);
mFailButton.setEnabled(false);
}
else {
mPassButton.setEnabled(false);
mFailButton.setEnabled(true);
}
}
}
private int getStageIndex()
{
final int stageIndex = getIntent().getIntExtra(STAGE_INDEX_EXTRA, 0);
return stageIndex;
}
private String getStageString(int stageIndex)
{
if (stageIndex == STAGE_APP_PICTURE) {
return "Application Picture";
}
if (stageIndex == STAGE_APP_VIDEO) {
return "Application Video";
}
if (stageIndex == STAGE_INTENT_PICTURE) {
return "Intent Picture";
}
if (stageIndex == STAGE_INTENT_VIDEO) {
return "Intent Video";
}
return "Unknown!!!";
}
private String getStageIntentString(int stageIndex)
{
if (stageIndex == STAGE_APP_PICTURE) {
return android.hardware.Camera.ACTION_NEW_PICTURE;
}
if (stageIndex == STAGE_APP_VIDEO) {
return android.hardware.Camera.ACTION_NEW_VIDEO;
}
if (stageIndex == STAGE_INTENT_PICTURE) {
return android.hardware.Camera.ACTION_NEW_PICTURE;
}
if (stageIndex == STAGE_INTENT_VIDEO) {
return android.hardware.Camera.ACTION_NEW_VIDEO;
}
return "Unknown Intent!!!";
}
private String getStageInstructionLabel(int stageIndex)
{
if (stageIndex == STAGE_APP_PICTURE) {
return getString(R.string.ci_instruction_text_app_picture_label);
}
if (stageIndex == STAGE_APP_VIDEO) {
return getString(R.string.ci_instruction_text_app_video_label);
}
if (stageIndex == STAGE_INTENT_PICTURE) {
return getString(R.string.ci_instruction_text_intent_picture_label);
}
if (stageIndex == STAGE_INTENT_VIDEO) {
return getString(R.string.ci_instruction_text_intent_video_label);
}
return "Unknown Instruction Label!!!";
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ci_main);
setPassFailButtonClickListeners();
setInfoResources(R.string.camera_intents, R.string.ci_info, -1);
mPassButton = (ImageButton) findViewById(R.id.pass_button);
mFailButton = (ImageButton) findViewById(R.id.fail_button);
mStartTestButton = (Button) findViewById(R.id.start_test_button);
mStartTestButton.setOnClickListener(this);
// This activity is reused multiple times
// to test each camera/intents combination
final int stageIndex = getIntent().getIntExtra(STAGE_INDEX_EXTRA, 0);
// Hitting the pass button goes to the next test activity.
// Only the last one uses the PassFailButtons click callback function,
// which gracefully terminates the activity.
if (stageIndex + 1 < NUM_STAGES) {
setPassButtonGoesToNextStage(stageIndex);
}
resetButtons();
// Set initial values
TextView intentsLabel =
(TextView) findViewById(R.id.intents_text);
intentsLabel.setText(
getString(R.string.ci_intents_label)
+ " "
+ Integer.toString(getStageIndex()+1)
+ " of "
+ Integer.toString(NUM_STAGES)
+ ": "
+ getStageIntentString(getStageIndex())
);
TextView instructionLabel =
(TextView) findViewById(R.id.instruction_text);
instructionLabel.setText(R.string.ci_instruction_text_photo_label);
/* Display the instructions to launch camera app and take a photo
*/
TextView cameraExtraLabel =
(TextView) findViewById(R.id.instruction_extra_text);
cameraExtraLabel.setText(getStageInstructionLabel(getStageIndex()));
mStartTestButton.setEnabled(true);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
onReceivedIntent(intent);
}
};
mFilterPicture = new IntentFilter();
mFilterPicture.addAction(Camera.ACTION_NEW_PICTURE);
mFilterPicture.addAction(Camera.ACTION_NEW_VIDEO);
try {
mFilterPicture.addDataType("video/*");
mFilterPicture.addDataType("image/*");
}
catch(IntentFilter.MalformedMimeTypeException e) {
Log.e(TAG, "Caught exceptione e " + e.toString());
}
registerReceiver(mReceiver, mFilterPicture);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.v(TAG, "onDestroy");
this.unregisterReceiver(mReceiver);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
/*
When testing INTENT_PICTURE, INTENT_VIDEO,
do not allow user to cheat by going to camera app and re-firing
the intents by taking a photo/video
*/
if (getStageIndex() == STAGE_INTENT_PICTURE ||
getStageIndex() == STAGE_INTENT_VIDEO) {
if (mActivityResult && mState == STATE_STARTED) {
mDetectCheating = true;
Log.w(TAG, "Potential cheating detected");
}
}
}
@Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
if (requestCode == 1337 + getStageIndex()) {
Log.v(TAG, "Activity we launched was finished");
mActivityResult = true;
if (mState != STATE_FAILED
&& getStageIndex() == STAGE_INTENT_PICTURE) {
mPassButton.setEnabled(true);
mFailButton.setEnabled(false);
mState = STATE_SUCCESSFUL;
/* successful, unless we get the mediastore intent back
at some point later on */
}
}
}
@Override
public String getTestDetails() {
return mReportBuilder.toString();
}
@Override
public void onClick(View view) {
Log.v(TAG, "Click detected");
if (view == mStartTestButton) {
Log.v(TAG, "Starting testing... ");
mState = STATE_STARTED;
/* we can allow user to fail immediately */
mFailButton.setEnabled(true);
/* trigger an ACTION_IMAGE_CAPTURE intent
which will run the camera app itself */
String intentStr = null;
Intent cameraIntent = null;
if (getStageIndex() == STAGE_INTENT_PICTURE) {
intentStr = android.provider.MediaStore.ACTION_IMAGE_CAPTURE;
}
else if (getStageIndex() == STAGE_INTENT_VIDEO) {
intentStr = android.provider.MediaStore.ACTION_VIDEO_CAPTURE;
}
if (intentStr != null) {
cameraIntent = new Intent(intentStr);
startActivityForResult(cameraIntent, 1337 + getStageIndex());
}
mStartTestButton.setEnabled(false);
}
if(view == mPassButton || view == mFailButton) {
final int stageIndex = getStageIndex();
for (int counter = 0; counter < NUM_STAGES; counter++) {
String combination = getStageString(counter) + "\n";
if(counter < stageIndex) {
// test already passed, or else wouldn't have made
// it to current stageIndex
mTestedCombinations.add(combination);
}
if(counter == stageIndex) {
// current test configuration
if(view == mPassButton) {
mTestedCombinations.add(combination);
}
else if(view == mFailButton) {
mUntestedCombinations.add(combination);
}
}
if(counter > stageIndex) {
// test not passed yet, since haven't made it to
// stageIndex
mUntestedCombinations.add(combination);
}
counter++;
}
mReportBuilder = new StringBuilder();
mReportBuilder.append("Passed combinations:\n");
for (String combination : mTestedCombinations) {
mReportBuilder.append(combination);
}
mReportBuilder.append("Failed/untested combinations:\n");
for (String combination : mUntestedCombinations) {
mReportBuilder.append(combination);
}
if(view == mPassButton) {
TestResult.setPassedResult(this, "CameraIntentsActivity",
getTestDetails());
}
if(view == mFailButton) {
TestResult.setFailedResult(this, "CameraIntentsActivity",
getTestDetails());
}
// restart activity to test next intents
Intent intent = new Intent(CameraIntentsActivity.this,
CameraIntentsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra(STAGE_INDEX_EXTRA, stageIndex + 1);
startActivity(intent);
}
}
private void resetButtons() {
enablePassFailButtons(false);
}
private void enablePassFailButtons(boolean enable) {
mPassButton.setEnabled(enable);
mFailButton.setEnabled(enable);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Auto-generated method stub
}
private void setPassButtonGoesToNextStage(final int stageIndex) {
findViewById(R.id.pass_button).setOnClickListener(this);
}
}