Home | History | Annotate | Download | only in Activities
      1 /*
      2  * Copyright (C) 2016 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 package com.android.cts.verifier.sensors.sixdof.Activities;
     17 
     18 import com.android.cts.verifier.R;
     19 import com.android.cts.verifier.sensors.sixdof.Activities.StartActivity.ResultCode;
     20 import com.android.cts.verifier.sensors.sixdof.Fragments.AccuracyFragment;
     21 import com.android.cts.verifier.sensors.sixdof.Fragments.ComplexMovementFragment;
     22 import com.android.cts.verifier.sensors.sixdof.Fragments.DataFragment;
     23 import com.android.cts.verifier.sensors.sixdof.Fragments.PhaseStartFragment;
     24 import com.android.cts.verifier.sensors.sixdof.Fragments.RobustnessFragment;
     25 import com.android.cts.verifier.sensors.sixdof.Interfaces.AccuracyListener;
     26 import com.android.cts.verifier.sensors.sixdof.Interfaces.BaseUiListener;
     27 import com.android.cts.verifier.sensors.sixdof.Interfaces.ComplexMovementListener;
     28 import com.android.cts.verifier.sensors.sixdof.Interfaces.RobustnessListener;
     29 import com.android.cts.verifier.sensors.sixdof.Utils.ReportExporter;
     30 import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
     31 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
     32 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
     33 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
     34 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
     35 import com.android.cts.verifier.sensors.sixdof.Utils.Manager.Lap;
     36 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
     37 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
     38 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
     39 import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseProvider;
     40 import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
     41 
     42 import android.content.Intent;
     43 import android.content.pm.ActivityInfo;
     44 import android.content.res.Configuration;
     45 import android.content.res.Resources;
     46 import android.os.Bundle;
     47 import android.app.Fragment;
     48 import android.app.FragmentManager;
     49 import android.app.FragmentTransaction;
     50 import android.app.AlertDialog;
     51 import android.app.Activity;
     52 import android.util.Log;
     53 import android.view.Display;
     54 import android.view.Menu;
     55 import android.view.MenuItem;
     56 import android.view.Surface;
     57 
     58 import java.io.IOException;
     59 import java.util.ArrayList;
     60 
     61 /**
     62  * Main Activity for 6DOF tests Handles calls between UI fragments and the Data fragment. The
     63  * controller in the MVC structure.
     64  */
     65 public class TestActivity extends Activity implements BaseUiListener, AccuracyListener,
     66         RobustnessListener, ComplexMovementListener {
     67 
     68     private static final String TAG = "TestActivity";
     69     private static final String TAG_DATA_FRAGMENT = "data_fragment";
     70     public static final String EXTRA_RESULT_ID = "extraResult";
     71     public static final String EXTRA_REPORT = "extraReport";
     72     public static final String EXTRA_ON_RESTART = "6dof_verifier_restart";
     73     public static final Object POSE_LOCK = new Object();
     74 
     75     private DataFragment mDataFragment;
     76 
     77     private BaseUiListener mUiListener;
     78     private AccuracyListener mAccuracyListener;
     79     private RobustnessListener mRobustnessListener;
     80     private ComplexMovementListener mComplexMovementListener;
     81 
     82     private CTSTest mCurrentTest = CTSTest.ACCURACY;
     83 
     84     private boolean mHasBeenPaused = false;
     85 
     86     public enum CTSTest {
     87         ACCURACY,
     88         ROBUSTNESS,
     89         COMPLEX_MOVEMENT
     90     }
     91 
     92     /**
     93      * Initialises camera preview, looks for a retained data fragment if we have one and adds UI
     94      * fragment.
     95      */
     96     @Override
     97     protected void onCreate(Bundle savedInstanceState) {
     98         super.onCreate(savedInstanceState);
     99 
    100         // If we are restarting, kill the test as data is invalid.
    101         if (savedInstanceState != null) {
    102             if (savedInstanceState.getBoolean(EXTRA_ON_RESTART)) {
    103                 Intent intent = this.getIntent();
    104                 intent.putExtra(EXTRA_RESULT_ID, ResultCode.FAILED_PAUSE_AND_RESUME);
    105                 this.setResult(RESULT_OK, intent);
    106                 finish();
    107             }
    108         }
    109 
    110         setContentView(R.layout.activity_cts);
    111 
    112         // Add the first instructions fragment.
    113         Fragment fragment = PhaseStartFragment.newInstance(CTSTest.ACCURACY);
    114         FragmentManager fragmentManager = getFragmentManager();
    115         FragmentTransaction transaction = fragmentManager.beginTransaction();
    116         transaction.replace(R.id.contentFragment, fragment);
    117         transaction.commit();
    118 
    119         mDataFragment = new DataFragment();
    120         fragmentManager.beginTransaction().add(mDataFragment, TAG_DATA_FRAGMENT).commit();
    121 
    122         // Lock the screen to its current rotation
    123         lockRotation();
    124     }
    125 
    126     /**
    127      * Lock the orientation of the device in its current state.
    128      */
    129     private void lockRotation() {
    130         final Display display = getWindowManager().getDefaultDisplay();
    131         int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
    132         int configOrientation = getResources().getConfiguration().orientation;
    133         switch (display.getRotation()) {
    134             case Surface.ROTATION_0:
    135             case Surface.ROTATION_180:
    136                 // We are currently in the same basic orientation as the natural orientation
    137                 naturalOrientation = configOrientation;
    138                 break;
    139             case Surface.ROTATION_90:
    140             case Surface.ROTATION_270:
    141                 // We are currently in the other basic orientation to the natural orientation
    142                 naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE) ?
    143                         Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
    144                 break;
    145         }
    146 
    147         int[] orientationMap = {
    148                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
    149                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
    150                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
    151                 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
    152         };
    153         // Since the map starts at portrait, we need to offset if this device's natural orientation
    154         // is landscape.
    155         int indexOffset = 0;
    156         if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
    157             indexOffset = 1;
    158         }
    159 
    160         // The map assumes default rotation. Check for reverse rotation and correct map if required
    161         try {
    162             if (getResources().getBoolean(getResources().getSystem().getIdentifier(
    163                     "config_reverseDefaultRotation", "bool", "android"))) {
    164                 orientationMap[0] = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
    165                 orientationMap[2] = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    166             }
    167         } catch (Resources.NotFoundException e) {
    168             // If resource is not found, assume default rotation and continue
    169             Log.d(TAG, "Cannot determine device rotation direction, assuming default");
    170         }
    171 
    172         setRequestedOrientation(orientationMap[(display.getRotation() + indexOffset) % 4]);
    173     }
    174 
    175     @Override
    176     public void onResume() {
    177         super.onResume();
    178 
    179         // 6DoF is reset after a recreation of activity, which invalidates the tests.
    180         if (mHasBeenPaused) {
    181             Intent intent = this.getIntent();
    182             intent.putExtra(EXTRA_RESULT_ID, ResultCode.FAILED_PAUSE_AND_RESUME);
    183             this.setResult(RESULT_OK, intent);
    184             finish();
    185         }
    186     }
    187 
    188     @Override
    189     public void onPause() {
    190         super.onPause();
    191         mHasBeenPaused = true;
    192     }
    193 
    194     @Override
    195     public boolean onCreateOptionsMenu(Menu menu) {
    196         // Inflate the menu; this adds items to the action bar if it is present.
    197         getMenuInflater().inflate(R.menu.menu_cts, menu);
    198         return true;
    199     }
    200 
    201     @Override
    202     public boolean onOptionsItemSelected(MenuItem item) {
    203         // Handle action bar item clicks here.
    204         int id = item.getItemId();
    205 
    206         switch (id) {
    207             case R.id.action_save_results:
    208                 saveResults();
    209                 return true;
    210             case R.id.action_xml:
    211                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
    212 
    213                 try {
    214                     builder.setMessage(mDataFragment.getTestReport().getContents())
    215                             .setTitle(R.string.results)
    216                             .setPositiveButton(R.string.got_it, null);
    217                 } catch (IOException e) {
    218                     Log.e(TAG, e.toString());
    219                 }
    220 
    221                 AlertDialog dialog = builder.create();
    222                 dialog.show();
    223                 return true;
    224             default:
    225                 return super.onOptionsItemSelected(item);
    226         }
    227     }
    228 
    229     public void saveResults() {
    230         try {
    231             new ReportExporter(this, getTestReport().getContents()).execute();
    232         } catch (IOException e) {
    233             Log.e(TAG, "Couldn't create test report.");
    234         }
    235     }
    236 
    237     public TestReport getTestReport() {
    238         return mDataFragment.getTestReport();
    239     }
    240 
    241     public void listenFor6DofData(Fragment listener) {
    242         mUiListener = (BaseUiListener) listener;
    243         switch (mCurrentTest) {
    244             case ACCURACY:
    245                 mAccuracyListener = (AccuracyListener) listener;
    246                 mRobustnessListener = null;
    247                 mComplexMovementListener = null;
    248                 break;
    249             case ROBUSTNESS:
    250                 mAccuracyListener = null;
    251                 mRobustnessListener = (RobustnessListener) listener;
    252                 mComplexMovementListener = null;
    253                 break;
    254             case COMPLEX_MOVEMENT:
    255                 mAccuracyListener = null;
    256                 mRobustnessListener = null;
    257                 mComplexMovementListener = (ComplexMovementListener) listener;
    258                 break;
    259             default:
    260                 throw new AssertionError("mCurrentTest is a test that doesn't exist!");
    261         }
    262     }
    263 
    264     public boolean isPoseProviderReady() {
    265         if (mDataFragment != null) {
    266             return mDataFragment.isPoseProviderReady();
    267         } else {
    268             return false;
    269         }
    270 
    271     }
    272 
    273     public ArrayList<Waypoint> getUserGeneratedWaypoints(Lap lap) {
    274         return mDataFragment.getUserGeneratedWaypoints(lap);
    275     }
    276 
    277     public Lap getLap() {
    278         return mDataFragment.getLap();
    279     }
    280 
    281     public ArrayList<Ring> getRings() {
    282         return mDataFragment.getRings();
    283     }
    284 
    285     @Override
    286     public void onPoseProviderReady() {
    287         if (mUiListener != null) {
    288             mUiListener.onPoseProviderReady();
    289         } else {
    290             Log.e(TAG, getString(R.string.error_null_fragment));
    291         }
    292 
    293         // Possible for this to be called while switching UI fragments, so mUiListener is null
    294         // but we want to start the test anyway.
    295         mDataFragment.testStarted();
    296     }
    297 
    298     @Override
    299     public void onWaypointPlaced() {
    300         if (mUiListener != null) {
    301             mUiListener.onWaypointPlaced();
    302         } else {
    303             Log.e(TAG, getString(R.string.error_null_fragment));
    304         }
    305     }
    306 
    307     @Override
    308     public void onResult(ResultObject result) {
    309         if (mUiListener != null) {
    310             mUiListener.onResult(result);
    311         } else {
    312             Log.e(TAG, getString(R.string.error_null_fragment));
    313         }
    314     }
    315 
    316     @Override
    317     public void onReset() {
    318         if (mAccuracyListener != null) {
    319             if (mCurrentTest == CTSTest.ACCURACY) {
    320                 mAccuracyListener.onReset();
    321             } else {
    322                 throw new RuntimeException("We are in the wrong test for this listener to be called.");
    323             }
    324         } else {
    325             Log.e(TAG, getString(R.string.error_null_fragment));
    326         }
    327     }
    328 
    329     @Override
    330     public void lap1Complete() {
    331         if (mAccuracyListener != null) {
    332             mAccuracyListener.lap1Complete();
    333         } else {
    334             Log.e(TAG, getString(R.string.error_null_fragment));
    335         }
    336     }
    337 
    338     public void attemptWaypointPlacement() throws WaypointAreaCoveredException, WaypointDistanceException, WaypointStartPointException, WaypointRingNotEnteredException {
    339         mDataFragment.onWaypointPlacementAttempt();
    340     }
    341 
    342     public void undoWaypointPlacement() {
    343         if (mDataFragment != null) {
    344             mDataFragment.undoWaypointPlacement();
    345         } else {
    346             Log.e(TAG, getString(R.string.error_retained_fragment_null));
    347         }
    348     }
    349 
    350     public void readyForLap2() {
    351         mDataFragment.startTest(CTSTest.ACCURACY);
    352     }
    353 
    354     public float getLatestDistanceData() {
    355         return mDataFragment.getLatestDistanceData();
    356     }
    357 
    358     public float getTimeRemaining() {
    359         return mDataFragment.getTimeRemaining();
    360     }
    361 
    362     public PoseProvider getPoseProvider() {
    363         return mDataFragment.getPoseProvider();
    364     }
    365 
    366     @Override
    367     public void onNewRotationData(RotationData data) {
    368         if (mRobustnessListener != null) {
    369             mRobustnessListener.onNewRotationData(data);
    370         } else {
    371             Log.e(TAG, getString(R.string.error_null_fragment));
    372         }
    373     }
    374 
    375     @Override
    376     public void onRingEntered(Ring ring) {
    377         if (mComplexMovementListener != null) {
    378             mComplexMovementListener.onRingEntered(ring);
    379         } else {
    380             Log.e(TAG, getString(R.string.error_null_fragment));
    381         }
    382     }
    383 
    384     /**
    385      * Loads test fragment for a particular phase.
    386      *
    387      * @param phase test to be started.
    388      */
    389     public void switchToTestFragment(CTSTest phase) {
    390         Log.d(TAG, "switchToTestFragment");
    391         Fragment fragment;
    392 
    393         switch (phase) {
    394             case ACCURACY:
    395                 fragment = AccuracyFragment.newInstance();
    396                 break;
    397             case ROBUSTNESS:
    398                 fragment = RobustnessFragment.newInstance();
    399                 break;
    400             case COMPLEX_MOVEMENT:
    401                 fragment = ComplexMovementFragment.newInstance(); //Complex Motion
    402                 break;
    403             default:
    404                 throw new AssertionError("Trying to start a test that doesn't exist!");
    405         }
    406         FragmentManager fm = getFragmentManager();
    407         FragmentTransaction transaction = fm.beginTransaction();
    408         transaction.replace(R.id.contentFragment, fragment);
    409         transaction.commit();
    410     }
    411 
    412     /**
    413      * Loads start instruction fragment for a particular test.
    414      *
    415      * @param phase test to show instruction screen for.
    416      */
    417     public void switchToStartFragment(CTSTest phase) {
    418         Log.e(TAG, "switchToStartFragment");
    419         mUiListener = null;
    420         mAccuracyListener = null;
    421         mRobustnessListener = null;
    422         mComplexMovementListener = null;
    423 
    424         mCurrentTest = phase;
    425         mDataFragment.startTest(mCurrentTest);
    426         Fragment fragment = PhaseStartFragment.newInstance(phase);
    427         FragmentManager fm = getFragmentManager();
    428         FragmentTransaction transaction = fm.beginTransaction();
    429         transaction.replace(R.id.contentFragment, fragment);
    430         transaction.commit();
    431     }
    432 
    433     @Override
    434     protected void onSaveInstanceState(Bundle outState) {
    435         // We are always going to be restarting if this is called.
    436         outState.putBoolean(EXTRA_ON_RESTART, true);
    437         super.onSaveInstanceState(outState);
    438     }
    439 
    440     @Override
    441     protected void onDestroy() {
    442         super.onDestroy();
    443         onDestroyUi();
    444         mUiListener = null;
    445         mAccuracyListener = null;
    446         mRobustnessListener = null;
    447         mComplexMovementListener = null;
    448         mDataFragment = null;
    449     }
    450 
    451     @Override
    452     public void onDestroyUi() {
    453         if (mUiListener != null) {
    454             mUiListener.onDestroyUi();
    455         }
    456     }
    457 }
    458