1 /*
2  * Copyright 2019 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 
17 package androidx.camera.integration.antelope
18 
19 import androidx.camera.integration.antelope.MainActivity.Companion.antelopeIdlingResource
20 import androidx.camera.integration.antelope.MainActivity.Companion.cameraParams
21 import androidx.camera.integration.antelope.MainActivity.Companion.isSingleTestRunning
22 import androidx.camera.integration.antelope.MainActivity.Companion.logd
23 import androidx.camera.integration.antelope.MainActivity.Companion.testsRemaining
24 import com.google.common.math.Stats
25 import java.text.SimpleDateFormat
26 import java.util.Calendar
27 import kotlin.collections.ArrayList
28 import kotlin.math.roundToInt
29 
30 /**
31  * During a multiple-test run, this should be called after each test is completed. Record the result
32  * and call the automatic test runner to start the next test.
33  *
34  * If all test are completed, post result to screen and save the log.
35  */
postTestResultsnull36 fun postTestResults(activity: MainActivity, testConfig: TestConfig) {
37     MainActivity.testRun.add(testConfig.testResults)
38     testsRemaining--
39 
40     var log = ""
41     var csv = ""
42 
43     if (0 >= testsRemaining) {
44         if (!isSingleTestRunning) {
45             log += testSummaryString(activity, MainActivity.testRun)
46             csv += testSummaryCSV(activity, MainActivity.testRun)
47         }
48 
49         for ((index, result) in MainActivity.testRun.withIndex()) {
50             // TODO with test summary we can combine these two cases
51             if (0 == index) {
52                 log += result.toString(activity, false)
53                 csv += result.toCSV(activity, false)
54             } else {
55                 log += result.toString(activity, false)
56                 csv += result.toCSV(activity, false)
57             }
58         }
59 
60         activity.resetUIAfterTest()
61         activity.updateLog(log)
62         writeCSV(activity, DeviceInfo().deviceShort, csv)
63 
64         // Indicate to Espresso that a test run has ended
65         try {
66             logd("Decrementing AntelopeIdlingResource")
67             antelopeIdlingResource.decrement()
68         } catch (ex: IllegalStateException) {
69             logd("Antelope idling resource decremented below 0. This should never happen.")
70         }
71     } else {
72         autoTestRunner(activity)
73     }
74 }
75 
76 /** Set up the TestConfig object for a single test */
createSingleTestConfignull77 fun createSingleTestConfig(activity: MainActivity): TestConfig {
78     val config = TestConfig()
79 
80     config.apply {
81         when (PrefHelper.getSingleTestType(activity)) {
82             "INIT" -> {
83                 testName = "Camera Open/Close"
84                 currentRunningTest = TestType.INIT
85             }
86             "PREVIEW" -> {
87                 testName = "Preview Start"
88                 currentRunningTest = TestType.PREVIEW
89             }
90             "SWITCH_CAMERA" -> {
91                 testName = "Switch Cameras"
92                 currentRunningTest = TestType.SWITCH_CAMERA
93             }
94             "MULTI_SWITCH" -> {
95                 testName = "Switch Cameras (Multiple)"
96                 currentRunningTest = TestType.MULTI_SWITCH
97             }
98             "MULTI_PHOTO" -> {
99                 testName = "Multiple Captures"
100                 currentRunningTest = TestType.MULTI_PHOTO
101             }
102             "MULTI_PHOTO_CHAIN" -> {
103                 testName = "Multiple Captures (Chained)"
104                 currentRunningTest = TestType.MULTI_PHOTO_CHAIN
105             }
106             else -> {
107                 testName = "Single Capture"
108                 currentRunningTest = TestType.PHOTO
109             }
110         }
111 
112         api = CameraAPI.valueOf(PrefHelper.getSingleTestApi(activity).uppercase())
113         focusMode = FocusMode.valueOf(PrefHelper.getSingleTestFocus(activity).uppercase())
114         imageCaptureSize =
115             ImageCaptureSize.valueOf(PrefHelper.getSingleTestImageSize(activity).uppercase())
116         camera = PrefHelper.getSingleTestCamera(activity)
117         config.setupTestResults()
118     }
119 
120     return config
121 }
122 
123 /** Create a test configuration given the test's name and the currently selected test values */
createTestConfignull124 fun createTestConfig(testName: String): TestConfig {
125     val config = TestConfig(testName)
126     config.camera = MainActivity.camViewModel.getCurrentCamera().value.toString()
127     config.api = MainActivity.camViewModel.getCurrentAPI().value ?: CameraAPI.CAMERA2
128     config.imageCaptureSize =
129         MainActivity.camViewModel.getCurrentImageCaptureSize().value ?: ImageCaptureSize.MAX
130 
131     // If we don't have auto-focus, we set the focus mode to FIXED
132     if (MainActivity.cameraParams.get(config.camera)?.hasAF ?: true)
133         config.focusMode = MainActivity.camViewModel.getCurrentFocusMode().value ?: FocusMode.AUTO
134     else config.focusMode = FocusMode.FIXED
135 
136     config.setupTestResults()
137 
138     return config
139 }
140 
141 /** For multiple tests, configure the list of TestConfigs to run */
setupAutoTestRunnernull142 fun setupAutoTestRunner(activity: MainActivity) {
143     MainActivity.autoTestConfigs.clear()
144     val cameras: ArrayList<String> = PrefHelper.getCameraIds(activity, MainActivity.cameraParams)
145     val logicalCameras: ArrayList<String> =
146         PrefHelper.getLogicalCameraIds(MainActivity.cameraParams)
147     val apis: ArrayList<CameraAPI> = PrefHelper.getAPIs(activity)
148     val imageSizes: ArrayList<ImageCaptureSize> = PrefHelper.getImageSizes(activity)
149     val focusModes: ArrayList<FocusMode> = PrefHelper.getFocusModes(activity)
150     val testTypes: ArrayList<TestType> = ArrayList()
151     val doSwitchTest: Boolean = PrefHelper.getSwitchTest(activity)
152 
153     testTypes.add(TestType.MULTI_PHOTO)
154     testTypes.add(TestType.MULTI_PHOTO_CHAIN)
155 
156     if (doSwitchTest) testTypes.add(TestType.MULTI_SWITCH)
157 
158     MainActivity.testRun.clear()
159 
160     for (camera in cameras) {
161         for (api in apis) {
162             // Camera1 does not have access to physical cameras, only logical 0 and 1
163             // Some devices have no camera or only 1 front-facing camera (like Chromebooks)
164             // so we need to make sure they exist
165             if ((CameraAPI.CAMERA1 == api) && !logicalCameras.contains(camera)) continue
166 
167             // Currently CameraX only supports FRONT and BACK
168             if ((CameraAPI.CAMERAX == api) && !(camera.equals("0") || camera.equals("1"))) continue
169 
170             for (imageSize in imageSizes) {
171                 for (focusMode in focusModes) {
172                     if (FocusMode.CONTINUOUS == focusMode) {
173                         // If camera is fixed-focus, only run the AUTO test
174                         if (!(MainActivity.cameraParams.get(camera)?.hasAF ?: true)) continue
175                     }
176 
177                     for (testType in testTypes) {
178                         // Camera1 does not have chaining capabilities
179                         if ((CameraAPI.CAMERA1 == api) && (TestType.MULTI_PHOTO_CHAIN == testType))
180                             continue
181 
182                         // For now we only test 0->1->0, just add this test for the first "camera"
183                         // TODO: figure out a way to test different permutations
184                         if ((TestType.MULTI_SWITCH == testType) && !camera.equals(cameras.first()))
185                             continue
186 
187                         // Switch test doesn't do a capture don't repeat for all capture sizes
188                         if (imageSize == ImageCaptureSize.MIN && testType == TestType.MULTI_SWITCH)
189                             continue
190 
191                         // Switch test doesn't do a capture so don't repeat for all focus modes
192                         if (focusMode != FocusMode.AUTO && testType == TestType.MULTI_SWITCH)
193                             continue
194 
195                         // If this is a fixed focus lens, focusMode here has been set to auto,
196                         // set it to fixed in the TestConfig
197                         var realFocusMode: FocusMode = focusMode
198                         if (!(MainActivity.cameraParams.get(camera)?.hasAF ?: true))
199                             realFocusMode = FocusMode.FIXED
200 
201                         var testName =
202                             when (api) {
203                                 CameraAPI.CAMERA1 -> "Camera1"
204                                 CameraAPI.CAMERA2 -> "Camera2"
205                                 CameraAPI.CAMERAX -> "CameraX"
206                             }
207 
208                         testName += " - "
209                         testName +=
210                             when (imageSize) {
211                                 ImageCaptureSize.MIN -> "Min"
212                                 ImageCaptureSize.MAX -> "Max"
213                             }
214                         testName += " image size - Camera device "
215 
216                         if (testType != TestType.MULTI_SWITCH) {
217                             testName += camera
218                             testName += " "
219                         }
220 
221                         testName +=
222                             when (realFocusMode) {
223                                 FocusMode.AUTO -> "(auto-focus)"
224                                 FocusMode.CONTINUOUS -> "(continuous focus)"
225                                 else -> "(fixed-focus)"
226                             }
227 
228                         testName += " - "
229                         testName +=
230                             when (testType) {
231                                 TestType.MULTI_PHOTO -> "Multiple Captures"
232                                 TestType.MULTI_PHOTO_CHAIN -> "Multiple Captures (chained)"
233                                 TestType.MULTI_SWITCH -> "Switch Camera"
234                                 else -> "unknown test"
235                             }
236 
237                         val testConfig =
238                             TestConfig(testName, testType, api, imageSize, realFocusMode, camera)
239                         testConfig.setupTestResults()
240                         MainActivity.autoTestConfigs.add(testConfig)
241                     }
242                 }
243             }
244         }
245     }
246 
247     // Add Test X of Y string to test names
248     for ((index, testConfig) in MainActivity.autoTestConfigs.withIndex()) {
249         testConfig.testName =
250             "" +
251                 (index + 1) +
252                 " of " +
253                 MainActivity.autoTestConfigs.size +
254                 ": " +
255                 testConfig.testName
256     }
257 
258     testsRemaining = MainActivity.autoTestConfigs.size
259 }
260 
261 /** Run the list of tests in autoTestConfigs */
autoTestRunnernull262 fun autoTestRunner(activity: MainActivity) {
263     if (MainActivity.cameras.isEmpty()) {
264         testsRemaining = 0
265         return
266     }
267 
268     // If something goes wrong or we are aborted, stop testing
269     if (0 == testsRemaining) return
270 
271     val currentTest: Int = MainActivity.autoTestConfigs.size - testsRemaining + 1
272     val currentConfig: TestConfig = MainActivity.autoTestConfigs.get(currentTest - 1)
273     MainActivity.logd("autoTestRun about to run: " + currentConfig.testName)
274 
275     activity.runOnUiThread {
276         if (testsRemaining == MainActivity.autoTestConfigs.size)
277             activity.setupUIForTest(currentConfig, false)
278         else activity.setupUIForTest(currentConfig, true)
279     }
280 
281     multiCounter = 0
282     initializeTest(activity, cameraParams.get(currentConfig.camera), currentConfig)
283 }
284 
285 /**
286  * For an array of TestResults, generate a high-level summary of the most important values.
287  *
288  * This mostly consists of values for default rear camera.
289  */
testSummaryStringnull290 fun testSummaryString(activity: MainActivity, allTestResults: ArrayList<TestResults>): String {
291     var output = ""
292 
293     if (allTestResults.isEmpty()) return output
294 
295     val mainCamera = getMainCamera(activity, allTestResults)
296 
297     val c2Auto =
298         findTest(
299             allTestResults,
300             mainCamera,
301             CameraAPI.CAMERA2,
302             ImageCaptureSize.MAX,
303             FocusMode.AUTO,
304             TestType.MULTI_PHOTO
305         )
306     val c2AutoChain =
307         findTest(
308             allTestResults,
309             mainCamera,
310             CameraAPI.CAMERA2,
311             ImageCaptureSize.MAX,
312             FocusMode.AUTO,
313             TestType.MULTI_PHOTO_CHAIN
314         )
315     val c2Caf =
316         findTest(
317             allTestResults,
318             mainCamera,
319             CameraAPI.CAMERA2,
320             ImageCaptureSize.MAX,
321             FocusMode.CONTINUOUS,
322             TestType.MULTI_PHOTO
323         )
324     val c2CafChain =
325         findTest(
326             allTestResults,
327             mainCamera,
328             CameraAPI.CAMERA2,
329             ImageCaptureSize.MAX,
330             FocusMode.CONTINUOUS,
331             TestType.MULTI_PHOTO_CHAIN
332         )
333     val c2AutoMin =
334         findTest(
335             allTestResults,
336             mainCamera,
337             CameraAPI.CAMERA2,
338             ImageCaptureSize.MIN,
339             FocusMode.AUTO,
340             TestType.MULTI_PHOTO
341         )
342     val c2Switch =
343         findTest(
344             allTestResults,
345             mainCamera,
346             CameraAPI.CAMERA2,
347             ImageCaptureSize.MAX,
348             FocusMode.AUTO,
349             TestType.MULTI_SWITCH
350         )
351     val c1Auto =
352         findTest(
353             allTestResults,
354             mainCamera,
355             CameraAPI.CAMERA1,
356             ImageCaptureSize.MAX,
357             FocusMode.AUTO,
358             TestType.MULTI_PHOTO
359         )
360     val c1Caf =
361         findTest(
362             allTestResults,
363             mainCamera,
364             CameraAPI.CAMERA1,
365             ImageCaptureSize.MAX,
366             FocusMode.CONTINUOUS,
367             TestType.MULTI_PHOTO
368         )
369     val c1Switch =
370         findTest(
371             allTestResults,
372             mainCamera,
373             CameraAPI.CAMERA1,
374             ImageCaptureSize.MAX,
375             FocusMode.AUTO,
376             TestType.MULTI_SWITCH
377         )
378     val cXAuto =
379         findTest(
380             allTestResults,
381             mainCamera,
382             CameraAPI.CAMERAX,
383             ImageCaptureSize.MAX,
384             FocusMode.AUTO,
385             TestType.MULTI_PHOTO
386         )
387     val cXCaf =
388         findTest(
389             allTestResults,
390             mainCamera,
391             CameraAPI.CAMERAX,
392             ImageCaptureSize.MAX,
393             FocusMode.CONTINUOUS,
394             TestType.MULTI_PHOTO
395         )
396     val cXSwitch =
397         findTest(
398             allTestResults,
399             mainCamera,
400             CameraAPI.CAMERAX,
401             ImageCaptureSize.MAX,
402             FocusMode.AUTO,
403             TestType.MULTI_SWITCH
404         )
405 
406     // Header
407     val dateFormatter = SimpleDateFormat("d MMM yyyy - kk'h'mm")
408     val cal: Calendar = Calendar.getInstance()
409     output +=
410         "DATE: " + dateFormatter.format(cal.time) + " (Antelope " + getVersionName(activity) + ")\n"
411 
412     output += "DEVICE: " + MainActivity.deviceInfo.device + "\n\n"
413     output += "CAMERAS:\n"
414     for (camera in MainActivity.cameras) output += camera + "\n"
415     output += "\n"
416 
417     // Test summary
418     output += "HIGH-LEVEL OVERVIEW:\n"
419 
420     output +=
421         "Capture (Cam2): " +
422             meanOfSumOfTwoArrays(c2Auto.capture, c2Auto.imageready) +
423             ", Cap chained (Cam2): " +
424             meanOfSumOfTwoArrays(c2AutoChain.capture, c2AutoChain.imageready) +
425             "\nCapture CAF (Cam2): " +
426             meanOfSumOfTwoArrays(c2Caf.capture, c2Caf.imageready) +
427             ", Chained CAF (Cam2): " +
428             meanOfSumOfTwoArrays(c2CafChain.capture, c2CafChain.imageready) +
429             "\nCapture (Cam1): " +
430             meanOfSumOfTwoArrays(c1Auto.capture, c1Auto.imageready) +
431             ", Cap CAF (Cam1): " +
432             meanOfSumOfTwoArrays(c1Caf.capture, c1Caf.imageready) +
433             "\nCapture (CamX): " +
434             meanOfSumOfTwoArrays(cXAuto.capture, cXAuto.imageready) +
435             ", Cap CAF (CamX): " +
436             meanOfSumOfTwoArrays(cXCaf.capture, cXCaf.imageready) +
437             "\nSwitch 1->2 (Cam2): " +
438             mean(c2Switch.switchToSecond) +
439             ", Switch 1->2 (Cam1): " +
440             mean(c1Switch.switchToSecond) +
441             ", Switch 1->2 (CamX): " +
442             mean(cXSwitch.switchToSecond) +
443             ", Switch 2->1 (Cam2): " +
444             mean(c2Switch.switchToFirst) +
445             ", Switch 2->1 (Cam1): " +
446             mean(c1Switch.switchToFirst) +
447             ", Switch 2->1 (CamX): " +
448             mean(cXSwitch.switchToFirst) +
449             "\nCam2 Open: " +
450             meanOfSumOfTwoArrays(c2Auto.initialization, c2Auto.previewStart) +
451             ", Cam1 Open: " +
452             meanOfSumOfTwoArrays(c1Auto.initialization, c1Auto.previewStart) +
453             "\nCam2 Close: " +
454             meanOfSumOfTwoArrays(c2Auto.previewClose, c2Auto.cameraClose) +
455             ", Cam1 Close: " +
456             meanOfSumOfTwoArrays(c1Auto.previewClose, c1Auto.cameraClose) +
457             "\n∆ Min to Max Size: " +
458             (numericalMean(c2Auto.capture) + numericalMean(c2Auto.imageready) -
459                 numericalMean(c2AutoMin.capture) -
460                 numericalMean(c2AutoMin.imageready)) +
461             ", Init->Image saved (Cam2): " +
462             mean(c2Auto.totalNoPreview) +
463             "\n"
464 
465     output += "\n"
466 
467     return output
468 }
469 
470 /**
471  * For an array of TestResults, generate a high-level summary of the most important values in a
472  * comma-separated .csv string.
473  *
474  * This mostly consists of values for default rear camera.
475  */
testSummaryCSVnull476 fun testSummaryCSV(activity: MainActivity, allTestResults: ArrayList<TestResults>): String {
477     var output = ""
478 
479     if (allTestResults.isEmpty()) return output
480 
481     val mainCamera = getMainCamera(activity, allTestResults)
482 
483     val c2Auto =
484         findTest(
485             allTestResults,
486             mainCamera,
487             CameraAPI.CAMERA2,
488             ImageCaptureSize.MAX,
489             FocusMode.AUTO,
490             TestType.MULTI_PHOTO
491         )
492     val c2AutoChain =
493         findTest(
494             allTestResults,
495             mainCamera,
496             CameraAPI.CAMERA2,
497             ImageCaptureSize.MAX,
498             FocusMode.AUTO,
499             TestType.MULTI_PHOTO_CHAIN
500         )
501     val c2Caf =
502         findTest(
503             allTestResults,
504             mainCamera,
505             CameraAPI.CAMERA2,
506             ImageCaptureSize.MAX,
507             FocusMode.CONTINUOUS,
508             TestType.MULTI_PHOTO
509         )
510     val c2CafChain =
511         findTest(
512             allTestResults,
513             mainCamera,
514             CameraAPI.CAMERA2,
515             ImageCaptureSize.MAX,
516             FocusMode.CONTINUOUS,
517             TestType.MULTI_PHOTO_CHAIN
518         )
519     val c2AutoMin =
520         findTest(
521             allTestResults,
522             mainCamera,
523             CameraAPI.CAMERA2,
524             ImageCaptureSize.MIN,
525             FocusMode.AUTO,
526             TestType.MULTI_PHOTO
527         )
528     val c2Switch =
529         findTest(
530             allTestResults,
531             mainCamera,
532             CameraAPI.CAMERA2,
533             ImageCaptureSize.MAX,
534             FocusMode.AUTO,
535             TestType.MULTI_SWITCH
536         )
537     val c1Auto =
538         findTest(
539             allTestResults,
540             mainCamera,
541             CameraAPI.CAMERA1,
542             ImageCaptureSize.MAX,
543             FocusMode.AUTO,
544             TestType.MULTI_PHOTO
545         )
546     val c1Caf =
547         findTest(
548             allTestResults,
549             mainCamera,
550             CameraAPI.CAMERA1,
551             ImageCaptureSize.MAX,
552             FocusMode.CONTINUOUS,
553             TestType.MULTI_PHOTO
554         )
555     val c1Switch =
556         findTest(
557             allTestResults,
558             mainCamera,
559             CameraAPI.CAMERA1,
560             ImageCaptureSize.MAX,
561             FocusMode.AUTO,
562             TestType.MULTI_SWITCH
563         )
564     val cXAuto =
565         findTest(
566             allTestResults,
567             mainCamera,
568             CameraAPI.CAMERAX,
569             ImageCaptureSize.MAX,
570             FocusMode.AUTO,
571             TestType.MULTI_PHOTO
572         )
573     val cXCaf =
574         findTest(
575             allTestResults,
576             mainCamera,
577             CameraAPI.CAMERAX,
578             ImageCaptureSize.MAX,
579             FocusMode.CONTINUOUS,
580             TestType.MULTI_PHOTO
581         )
582     val cXSwitch =
583         findTest(
584             allTestResults,
585             mainCamera,
586             CameraAPI.CAMERAX,
587             ImageCaptureSize.MAX,
588             FocusMode.AUTO,
589             TestType.MULTI_SWITCH
590         )
591 
592     // Header
593     val dateFormatter = SimpleDateFormat("d MMM yyyy - kk'h'mm")
594     val cal: Calendar = Calendar.getInstance()
595     output +=
596         "DATE: " +
597             dateFormatter.format(cal.time) +
598             " (Antelope " +
599             getVersionName(activity) +
600             ")" +
601             "\n"
602 
603     output += "DEVICE: " + MainActivity.deviceInfo.device + "\n" + "\n"
604     output += "CAMERAS: " + "\n"
605     for (camera in MainActivity.cameras) output += camera + "\n"
606     output += "\n"
607 
608     // Test summary
609     output += "HIGH-LEVEL OVERVIEW:\n"
610 
611     output += ","
612     output +=
613         "Capture (Cam2)" +
614             "," +
615             "Cap chained (Cam2)" +
616             "," +
617             "Capture CAF (Cam2)" +
618             "," +
619             "Chained CAF (Cam2)" +
620             "," +
621             "Capture (Cam1)" +
622             "," +
623             "Cap CAF (Cam1)" +
624             "," +
625             "Capture (CamX)" +
626             "," +
627             "Cap CAF (CamX)" +
628             "," +
629             "Switch 1->2 (Cam2)" +
630             "," +
631             "Switch 1->2 (Cam1)" +
632             "," +
633             "Switch 1->2 (CamX)" +
634             "," +
635             "Switch 2->1 (Cam2)" +
636             "," +
637             "Switch 2->1 (Cam1)" +
638             "," +
639             "Switch 2->1 (CamX)" +
640             "," +
641             "Cam2 Open" +
642             "," +
643             "Cam1 Open" +
644             "," +
645             "Cam2 Close" +
646             "," +
647             "Cam1 Close" +
648             "," +
649             "∆ Min to Max Size" +
650             "," +
651             "Init->Image saved (Cam2)" +
652             "\n"
653 
654     output += ","
655     output +=
656         "" +
657             meanOfSumOfTwoArrays(c2Auto.capture, c2Auto.imageready) +
658             "," +
659             meanOfSumOfTwoArrays(c2AutoChain.capture, c2AutoChain.imageready)
660     output +=
661         "," +
662             meanOfSumOfTwoArrays(c2Caf.capture, c2Caf.imageready) +
663             "," +
664             meanOfSumOfTwoArrays(c2CafChain.capture, c2CafChain.imageready)
665     output +=
666         "," +
667             meanOfSumOfTwoArrays(c1Auto.capture, c1Auto.imageready) +
668             "," +
669             meanOfSumOfTwoArrays(c1Caf.capture, c1Caf.imageready)
670     output +=
671         "," +
672             meanOfSumOfTwoArrays(cXAuto.capture, cXAuto.imageready) +
673             "," +
674             meanOfSumOfTwoArrays(cXCaf.capture, cXCaf.imageready)
675     output += "," + mean(c2Switch.switchToSecond) + "," + mean(c1Switch.switchToSecond)
676     output += "," + mean(cXSwitch.switchToSecond)
677     output += "," + mean(c2Switch.switchToFirst) + "," + mean(c1Switch.switchToFirst)
678     output += "," + mean(cXSwitch.switchToFirst)
679     output +=
680         "," +
681             meanOfSumOfTwoArrays(c2Auto.initialization, c2Auto.previewStart) +
682             "," +
683             meanOfSumOfTwoArrays(c1Auto.initialization, c1Auto.previewStart)
684     output +=
685         "," +
686             meanOfSumOfTwoArrays(c2Auto.previewClose, c2Auto.cameraClose) +
687             "," +
688             meanOfSumOfTwoArrays(c1Auto.previewClose, c1Auto.cameraClose)
689     output +=
690         "," +
691             (numericalMean(c2Auto.capture) + numericalMean(c2Auto.imageready) -
692                 numericalMean(c2AutoMin.capture) -
693                 numericalMean(c2AutoMin.imageready))
694     output += "," + mean(c2Auto.totalNoPreview) + "\n"
695 
696     output += "\n"
697     return output
698 }
699 
700 /** Search an array of TestResults for the first test result that matches the given parameters */
findTestnull701 fun findTest(
702     allTestResults: ArrayList<TestResults>,
703     camera: String,
704     api: CameraAPI,
705     imageCaptureSize: ImageCaptureSize,
706     focusMode: FocusMode,
707     testType: TestType
708 ): TestResults {
709 
710     for (testResult in allTestResults) {
711         // Look for the matching test result
712         if (
713             testResult.camera.equals(camera) &&
714                 testResult.cameraAPI == api &&
715                 testResult.imageCaptureSize == imageCaptureSize &&
716                 (testResult.focusMode == focusMode || testResult.focusMode == FocusMode.FIXED) &&
717                 testResult.testType == testType
718         ) {
719             return testResult
720         }
721     }
722 
723     // Return empty test result
724     return TestResults()
725 }
726 
727 /** The mean of a given array of longs, as a string */
meannull728 fun mean(array: ArrayList<Long>): String {
729     if (array.isEmpty()) {
730         return "n/a"
731     } else return Stats.meanOf(array).roundToInt().toString()
732 }
733 
734 /** The mean of a given array of longs, as a double */
numericalMeannull735 fun numericalMean(array: ArrayList<Long>): Double {
736     if (array.isEmpty()) return 0.0 else return Stats.meanOf(array)
737 }
738 
739 /** The mean of two arrays of longs added together, as a string */
meanOfSumOfTwoArraysnull740 fun meanOfSumOfTwoArrays(array1: ArrayList<Long>, array2: ArrayList<Long>): String {
741     if (array1.isEmpty() && array2.isEmpty()) {
742         return "n/a"
743     }
744     if (array1.isEmpty()) return mean(array2)
745     if (array2.isEmpty()) return mean(array1)
746     else return (Stats.meanOf(array1) + Stats.meanOf(array2)).roundToInt().toString()
747 }
748 
749 /**
750  * Find the "main" camera id, priority is: first rear facing physical, first rear-facing logical,
751  * first camera in the system.
752  */
getMainCameranull753 fun getMainCamera(activity: MainActivity, allTestResults: ArrayList<TestResults>): String {
754     val mainCamera = allTestResults.first().cameraId
755 
756     // Return the first rear-facing camera
757     for (param in MainActivity.cameraParams) {
758         if (!param.value.isFront && !param.value.isExternal)
759 
760         // If only logical cameras, first rear-facing is fine
761         if (PrefHelper.getOnlyLogical(activity)) {
762                 logd("The MAIN camera id is:" + param.value.id)
763                 return param.value.id
764 
765                 // Otherwise, make sure this is a physical camera
766             } else {
767                 if (param.value.physicalCameras.contains(param.value.id)) {
768                     logd("The MAIN camera id is:" + param.value.id)
769                     return param.value.id
770                 }
771             }
772     }
773 
774     return mainCamera
775 }
776 
777 /** Return the version name of the Activity */
778 @Suppress("DEPRECATION")
getVersionNamenull779 fun getVersionName(activity: MainActivity): String? {
780     val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
781     return packageInfo.versionName
782 }
783