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.Utils;
17 
18 import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
19 import com.android.cts.verifier.sensors.sixdof.Interfaces.AccuracyListener;
20 import com.android.cts.verifier.sensors.sixdof.Interfaces.BaseUiListener;
21 import com.android.cts.verifier.sensors.sixdof.Interfaces.ComplexMovementListener;
22 import com.android.cts.verifier.sensors.sixdof.Interfaces.RobustnessListener;
23 import com.android.cts.verifier.sensors.sixdof.Renderer.BaseRenderer;
24 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
25 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
26 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointException;
27 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
28 import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
29 import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
30 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
31 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
32 import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
33 import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseData;
34 import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
35 import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.AccuracyTest;
36 import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.ComplexMovementTest;
37 import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.RobustnessTest;
38 
39 import android.content.Context;
40 
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 
44 /**
45  * Manages all of the tests.
46  */
47 public class Manager {
48     private Lap mLap = Lap.LAP_1;
49     public static final int MAX_MARKER_NUMBER = 5;
50     private ReferencePath mReferencePath = new ReferencePath();
51     private AccuracyTest mAccuracyTest;
52     private RobustnessTest mRobustnessTest;
53     private ComplexMovementTest mComplexMovementTest;
54     private TestReport mTestReport;
55     private float mRemainingPath;
56     private long mTimeRemaining;
57 
58     public enum Lap {
59         LAP_1,
60         LAP_2,
61         LAP_3,
62         LAP_4,
63     }
64 
65     private ComplexMovementListener mComplexMovementListener;
66     private RobustnessListener mRobustnessListener;
67     private AccuracyListener mAccuracyListener;
68     private BaseUiListener mBaseUiListener;
69 
70     /**
71      * Links the listeners to the activity.
72      *
73      * @param context reference to the activity.
74      */
setupListeners(Context context)75     public void setupListeners(Context context) {
76         mAccuracyListener = (AccuracyListener) context;
77         mRobustnessListener = (RobustnessListener) context;
78         mComplexMovementListener = (ComplexMovementListener) context;
79         mBaseUiListener = (BaseUiListener) context;
80     }
81 
82     /**
83      * Removes the references to the activity so that the activity can be properly terminated.
84      */
stopListening()85     public void stopListening() {
86         mRobustnessListener = null;
87         mAccuracyListener = null;
88         mBaseUiListener = null;
89         mComplexMovementListener = null;
90     }
91 
ringEntered(Ring ring)92     public void ringEntered(Ring ring) {
93         mComplexMovementListener.onRingEntered(ring);
94     }
95 
96     /**
97      * Indicated that the pose provider is ready.
98      */
onPoseProviderReady()99     public void onPoseProviderReady() {
100         mBaseUiListener.onPoseProviderReady();
101     }
102 
103     /**
104      * Constructor for the class.
105      *
106      * @param testReport a reference to the test report to be used to record failures.
107      */
Manager(TestReport testReport)108     public Manager(TestReport testReport) {
109         mTestReport = testReport;
110     }
111 
112     /**
113      * Adds the waypoint data to the appropriate path.
114      *
115      * @param coordinates   the coordinates to use for the waypoint.
116      * @param userGenerated indicates whether the data was user created or system created.
117      * @throws WaypointDistanceException    if the location is too close to another.
118      * @throws WaypointAreaCoveredException if the area covered by the user is too little.
119      * @throws WaypointStartPointException  if the location is not close enough to the start.
120      */
addPoseDataToPath( float[] coordinates, boolean userGenerated)121     public void addPoseDataToPath(
122             float[] coordinates, boolean userGenerated)
123             throws WaypointAreaCoveredException, WaypointDistanceException,
124             WaypointStartPointException, WaypointRingNotEnteredException {
125         switch (mLap) {
126             case LAP_1:
127                 try {
128                     mReferencePath.createWaypointAndAddToPath(coordinates, userGenerated, mLap);
129                 } catch (WaypointStartPointException exception) {
130                     float[] initialCoords = mReferencePath.getPathMarkers().get(0).getCoordinates();
131                     String initialWaypointCoords =
132                             MathsUtils.coordinatesToString(initialCoords);
133                     String distance = String.valueOf(
134                             MathsUtils.distanceCalculationInXYZSpace(
135                                     initialCoords, coordinates));
136                     String details = "Not close enough to initial waypoint:\n"
137                             + "Distance:"
138                             + distance
139                             + "\nInitial Waypoint Coordinates: "
140                             + initialWaypointCoords
141                             + "\nAttempted placement coordinates: "
142                             + MathsUtils.coordinatesToString(coordinates);
143                     mTestReport.setFailDetails(details);
144 
145                     // We still need to give the exception to UI to display message.
146                     throw exception;
147                 }
148 
149                 if (mReferencePath.getPathMarkersSize() == MAX_MARKER_NUMBER) {
150                     mAccuracyListener.lap1Complete();
151                 }
152                 break;
153             case LAP_2:
154                 mAccuracyTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
155                 break;
156             case LAP_3:
157                 mRobustnessTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
158                 break;
159             case LAP_4:
160                 mComplexMovementTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
161                 break;
162             default:
163                 throw new AssertionError("addPoseDataToPath default: Unrecognised lap", null);
164         }
165         if (userGenerated) {
166             mBaseUiListener.onWaypointPlaced();
167         }
168     }
169 
170     /**
171      * Removes the last marker from the current lap.
172      */
removeLastAddedMarker()173     public void removeLastAddedMarker() {
174         boolean resetTest;
175         switch (mLap) {
176             case LAP_1:
177                 resetTest = mReferencePath.removeLastMarker();
178                 break;
179             case LAP_2:
180                 resetTest = mAccuracyTest.removeLastAddedMarker();
181                 break;
182             case LAP_3:
183                 resetTest = mRobustnessTest.removeLastAddedMarker();
184                 break;
185             case LAP_4:
186                 resetTest = mComplexMovementTest.removeLastAddedMarker();
187                 break;
188             default:
189                 throw new AssertionError("removeLastAddedMarker default: Unrecognised lap", null);
190         }
191         if (resetTest) {
192             mAccuracyListener.onReset();
193         }
194     }
195 
196     /**
197      * Initiates the accuracy test.
198      */
startAccuracyTest()199     public void startAccuracyTest() {
200         mAccuracyTest = new AccuracyTest(mReferencePath, mTestReport, this);
201         mLap = Lap.LAP_2;
202     }
203 
204     /**
205      * Initiates the robustness test.
206      */
startRobustnessTest()207     public void startRobustnessTest() {
208         mRobustnessTest = new RobustnessTest(mReferencePath, mTestReport, this,
209                 BaseRenderer.getDeviceRotation((Context) mBaseUiListener));
210         mLap = Lap.LAP_3;
211 
212     }
213 
214     /**
215      * Initiates the complex movement test.
216      */
startComplexMovementTest()217     public void startComplexMovementTest() {
218         mComplexMovementTest = new ComplexMovementTest(mReferencePath, mTestReport, this);
219         mLap = Lap.LAP_4;
220     }
221 
222     /**
223      * Indicates that the accuracy test has been completed.
224      *
225      * @param passList A list to indicate whether the test passes or not.
226      */
onAccuracyTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> passList)227     public void onAccuracyTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> passList) {
228         mBaseUiListener.onResult(new ResultObject(passList));
229     }
230 
231     /**
232      * Indicates that the robustness test has been completed.
233      *
234      * @param robustnessTestResults List containing information about whether the tests failed or
235      *                              passed.
236      */
onRobustnessTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> robustnessTestResults)237     public void onRobustnessTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> robustnessTestResults) {
238         ResultObject robustnessResult = new ResultObject(robustnessTestResults);
239         mBaseUiListener.onResult(robustnessResult);
240     }
241 
242     /**
243      * Indicates that the complex movement test has been completed.
244      *
245      * @param complexMovementTestResults List containing information about whether the tests failed
246      *                                   or passed.
247      */
onComplexMovementTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> complexMovementTestResults)248     public void onComplexMovementTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> complexMovementTestResults) {
249         ResultObject complexMovementResult = new ResultObject(complexMovementTestResults);
250 
251         if (complexMovementResult.hasPassed()) {
252             mTestReport.setTestState(TestReport.TestStatus.PASS);
253         }
254 
255         mBaseUiListener.onResult(complexMovementResult);
256     }
257 
258     /**
259      * Sets the path remaining for the user to travel.
260      */
calculateRemainingPath()261     public void calculateRemainingPath() {
262         mRemainingPath = mReferencePath.calculatePathRemaining();
263     }
264 
265     /**
266      * Uses the current rotation and location to calculate the rotation detail's. Also gives the UI
267      * information about the rotation.
268      *
269      * @param rotations   Quaternion containing the current rotation.
270      * @param translation The location the rotation occurred.
271      */
calculateRotationData(float[] rotations, float[] translation)272     public void calculateRotationData(float[] rotations, float[] translation) {
273         RotationData rotationData = mRobustnessTest.getRotationData(rotations, translation);
274         if (rotationData != null) {
275             mRobustnessListener.onNewRotationData(rotationData);
276         }
277     }
278 
279     /**
280      * Sets the time remaining to place a waypoint.
281      */
calculateTimeRemaining()282     public void calculateTimeRemaining() {
283         mTimeRemaining = mRobustnessTest.getTimeRemaining();
284     }
285 
286     /**
287      * Handles new pose data.
288      *
289      * @param currentPose The current pose data.
290      */
onNewPoseData(PoseData currentPose)291     public void onNewPoseData(PoseData currentPose) {
292         if (mReferencePath.getCurrentPathSize() != 0) {
293             switch (mLap) {
294                 case LAP_1:
295                     calculateRemainingPath();
296                     break;
297                 case LAP_2:
298                     break;
299                 case LAP_3:
300                     if (mRobustnessTest.getTestPathMarkersSize() > 0) {
301                         calculateTimeRemaining();
302                         calculateRotationData(currentPose.getRotationAsFloats(), currentPose.getTranslationAsFloats());
303                     }
304                     break;
305                 case LAP_4:
306                     mComplexMovementTest.checkIfARingHasBeenPassed(currentPose.getTranslationAsFloats());
307                     break;
308             }
309             try {
310                 addPoseDataToPath(currentPose.getTranslationAsFloats(),
311                         false);
312             } catch (WaypointException e) {
313                 throw new AssertionError(
314                         "System added waypoint should not be validated", e);
315             }
316         }
317     }
318 
319     /**
320      * Returns the distance remaining to travel by the user.
321      */
getRemainingPath()322     public float getRemainingPath() {
323         return mRemainingPath;
324     }
325 
326     /**
327      * Returns the makers in the reference path.
328      */
getReferencePathMarkers()329     public ArrayList<Waypoint> getReferencePathMarkers() {
330         return mReferencePath.getPathMarkers();
331     }
332 
333     /**
334      * Returns the makers in the accuracy test path.
335      */
getTestPathMarkers()336     public ArrayList<Waypoint> getTestPathMarkers() {
337         return mAccuracyTest.getTestPathMarkers();
338     }
339 
340     /**
341      * Returns the time remaining to place the marker.
342      */
getTimeRemaining()343     public long getTimeRemaining() {
344         return mTimeRemaining;
345     }
346 
347     /**
348      * Returns the markers in the robustness test path.
349      */
getRobustnessMarker()350     public ArrayList<Waypoint> getRobustnessMarker() {
351         return mRobustnessTest.getTestPathMarkers();
352     }
353 
354     /**
355      * Returns the current phase of the test.
356      */
getLap()357     public Lap getLap() {
358         return mLap;
359     }
360 
361     /**
362      * Returns the rings in the ComplexMovement path.
363      */
getRings()364     public ArrayList<Ring> getRings() {
365         return mComplexMovementTest.getRings();
366     }
367 
368     /**
369      * Returns the makers in the ComplexMovement test path.
370      */
getComplexMovementTestMarkers()371     public ArrayList<Waypoint> getComplexMovementTestMarkers() {
372         return mComplexMovementTest.getTestPathMarkers();
373     }
374 }
375