1 /*
2  * Copyright (C) 2021 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 android.companion.cts.common
18 
19 import android.app.Activity
20 import android.content.Context
21 import android.content.Intent
22 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
23 import android.content.IntentSender
24 import android.os.Bundle
25 import android.util.Log
26 import kotlin.time.Duration.Companion.milliseconds
27 import kotlin.time.Duration.Companion.seconds
28 
29 /**
30  * An [Activity] for launching confirmation UI via an [android.content.IntentSender.sendIntent] and
31  * receiving result in [onActivityResult].
32  */
33 class CompanionActivity : Activity() {
34     private var result: Pair<Int, Intent?>? = null
35 
onCreatenull36     override fun onCreate(savedInstanceState: Bundle?) {
37         Log.d(TAG, "$this.onCreate()")
38         super.onCreate(savedInstanceState)
39         unsafeInstance = this
40     }
41 
onDestroynull42     override fun onDestroy() {
43         Log.d(TAG, "$this.onDestroy()")
44         unsafeInstance = null
45         super.onDestroy()
46     }
47 
onActivityResultnull48     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
49         Log.i(TAG, "onActivityResult() code=${resultCode.codeToString()}, " +
50                 "data=${intent.extras}")
51         result = resultCode to data
52     }
53 
54     companion object {
55         private var unsafeInstance: CompanionActivity? = null
56         val instance: CompanionActivity
57             get() = unsafeInstance ?: error("There is no CompanionActivity")
58 
launchAndWaitnull59         fun launchAndWait(context: Context) {
60             val intent = Intent(context, CompanionActivity::class.java)
61             intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
62             context.startActivity(intent)
63 
64             waitForResult(timeout = 3.seconds, interval = 100.milliseconds) {
65                 unsafeInstance?.takeIf { it.isResumed }
66             } ?: error("CompanionActivity has not appeared")
67         }
68 
startIntentSendernull69         fun startIntentSender(intentSender: IntentSender) =
70                 instance.startIntentSenderForResult(intentSender, 0, null, 0, 0, 0)
71 
72         fun waitForActivityResult(): Pair<Int, Intent?> {
73             val result = waitForResult(timeout = 1.seconds, interval = 100.milliseconds) {
74                 instance.result
75             } ?: error("onActivityResult() has not been invoked")
76             instance.result = null
77             return result
78         }
79 
finishnull80         fun finish() = instance.finish()
81 
82         fun safeFinish() = unsafeInstance?.finish()
83 
84         fun waitUntilGone() = waitFor { unsafeInstance == null }
85     }
86 }
87 
codeToStringnull88 private fun Int.codeToString() = when (this) {
89     Activity.RESULT_OK -> "RESULT_OK"
90     Activity.RESULT_CANCELED -> "RESULT_CANCELED"
91     else -> "Unknown"
92 } + "($this)"
93