• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.onboarding.nodes.testing.testapp
2 
3 import android.content.Context
4 import android.content.Intent
5 import android.content.Intent.CATEGORY_DEFAULT
6 import android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT
7 import android.content.pm.PackageManager.ResolveInfoFlags
8 import android.graphics.Color
9 import android.os.Bundle
10 import android.widget.ArrayAdapter
11 import android.widget.Button
12 import android.widget.EditText
13 import android.widget.LinearLayout
14 import android.widget.Spinner
15 import android.widget.TextView
16 import androidx.activity.result.ActivityResult
17 import com.android.onboarding.activity.CompositeOnboardingActivity
18 import com.android.onboarding.common.TEST_APP
19 import com.android.onboarding.contracts.IdentifyExecutingContractByAction
20 import com.android.onboarding.contracts.OnboardingActivityApiContract
21 import com.android.onboarding.contracts.annotations.DiscouragedOnboardingApi
22 import com.android.onboarding.contracts.annotations.InternalOnboardingApi
23 import com.android.onboarding.contracts.annotations.OnboardingNode
24 import com.android.onboarding.contracts.failNode
25 import com.android.onboarding.contracts.registerForActivityLaunch
26 
27 @OptIn(DiscouragedOnboardingApi::class, InternalOnboardingApi::class)
28 class MainActivity : CompositeOnboardingActivity<ContractArg, String, ColourContract>() {
29   private val defaultContract = RedContract()
30   private val contracts = arrayOf(defaultContract, BlueContract(), GreenContract())
31 
selectContractnull32   override fun selectContract(intent: Intent): ColourContract =
33     contracts.firstOrNull { it.isExecuting(intent.action) } ?: defaultContract
34 
onCreatenull35   override fun onCreate(savedInstanceState: Bundle?) {
36     super.onCreate(savedInstanceState)
37 
38     setContentView(R.layout.activity_main)
39 
40     if (attachResult.valid) {
41       @Suppress("SetTextI18n")
42       checkNotNull(findViewById<TextView>(R.id.status)).text = "Received argument: ${argument.arg}"
43     }
44 
45     val processSpinner = checkNotNull(findViewById<Spinner>(R.id.processSpinner))
46     val packages =
47       packageManager
48         .queryIntentActivities(
49           Intent("com.android.onboarding.nodes.testing.testapp.red"),
50           ResolveInfoFlags.of(0),
51         )
52         .map { it.activityInfo.packageName }
53         .toSet()
54         .toList()
55     val packageNames =
56       packages
57         .map { packageManager.getApplicationLabel(packageManager.getApplicationInfo(it, 0)) }
58         .toList()
59     processSpinner.adapter =
60       ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, packageNames)
61     processSpinner.setSelection(packages.indexOf(packageName))
62 
63     val contractSpinner = checkNotNull(findViewById<Spinner>(R.id.contractSpinner))
64     contractSpinner.adapter =
65       ArrayAdapter(
66         this,
67         android.R.layout.simple_spinner_dropdown_item,
68         contracts.map(OnboardingActivityApiContract<*, *>::metadata).map(OnboardingNode::name),
69       )
70     contractSpinner.setSelection(contracts.indexOf(contract))
71 
72     val noResultLaunchers = contracts.map { registerForActivityLaunch(it) }
73     val resultLaunchers = contracts.map { registerForActivityResult(it, this::onResult) }
74 
75     checkNotNull(findViewById<TextView>(R.id.packageName)).text =
76       packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, 0))
77 
78     checkNotNull(findViewById<LinearLayout>(R.id.bg)).setBackgroundColor(contract.colour)
79 
80     when (contract) {
81       is RedContract -> {
82         checkNotNull(findViewById<LinearLayout>(R.id.bg)).setBackgroundColor(Color.RED)
83       }
84       is BlueContract -> {
85         checkNotNull(findViewById<LinearLayout>(R.id.bg)).setBackgroundColor(Color.BLUE)
86       }
87       is GreenContract -> {
88         checkNotNull(findViewById<LinearLayout>(R.id.bg)).setBackgroundColor(Color.GREEN)
89       }
90     }
91 
92     checkNotNull(findViewById<Button>(R.id.startActivityAndFinishButton)).setOnClickListener {
93       val arg = checkNotNull(findViewById<EditText>(R.id.argument)).text.toString()
94       val contractId =
95         checkNotNull(findViewById<Spinner>(R.id.contractSpinner)).selectedItemId.toInt()
96       val contractLauncher = noResultLaunchers[contractId]
97       val targetPackageName = checkNotNull(packages[processSpinner.selectedItemPosition]).toString()
98 
99       contractLauncher.launch(ContractArg(arg, targetPackageName))
100       finish()
101     }
102 
103     checkNotNull(findViewById<Button>(R.id.startActivityAndForwardButton)).setOnClickListener {
104       val arg = checkNotNull(findViewById<EditText>(R.id.argument)).text.toString()
105       val contractId =
106         checkNotNull(findViewById<Spinner>(R.id.contractSpinner)).selectedItemId.toInt()
107       val contractLauncher = noResultLaunchers[contractId]
108       val targetPackageName = checkNotNull(packages[processSpinner.selectedItemPosition]).toString()
109 
110       contractLauncher.launch(ContractArg(arg, targetPackageName, shouldForward = true))
111       finish()
112     }
113 
114     checkNotNull(findViewById<Button>(R.id.startActivityButton)).setOnClickListener {
115       val arg = checkNotNull(findViewById<EditText>(R.id.argument)).text.toString()
116       val contractId =
117         checkNotNull(findViewById<Spinner>(R.id.contractSpinner)).selectedItemId.toInt()
118       val contractLauncher = noResultLaunchers[contractId]
119       val targetPackageName = checkNotNull(packages[processSpinner.selectedItemPosition]).toString()
120 
121       contractLauncher.launch(ContractArg(arg, targetPackageName))
122     }
123 
124     checkNotNull(findViewById<Button>(R.id.startActivityForResultButton)).setOnClickListener {
125       val arg = checkNotNull(findViewById<EditText>(R.id.argument)).text.toString()
126       val contractId =
127         checkNotNull(findViewById<Spinner>(R.id.contractSpinner)).selectedItemId.toInt()
128       val contractLauncher = resultLaunchers[contractId]
129       val targetPackageName = checkNotNull(packages[processSpinner.selectedItemPosition]).toString()
130 
131       contractLauncher.launch(ContractArg(arg, targetPackageName))
132     }
133 
134     checkNotNull(findViewById<Button>(R.id.finishButton)).setOnClickListener { finish() }
135     checkNotNull(findViewById<Button>(R.id.crashButton)).setOnClickListener {
136       error("Crashing, as requested...")
137     }
138     checkNotNull(findViewById<Button>(R.id.failNodeButton)).setOnClickListener {
139       failNode(checkNotNull(findViewById<EditText>(R.id.argument)).text.toString())
140     }
141     checkNotNull(findViewById<Button>(R.id.setResultAndFinishButton)).setOnClickListener {
142       setResult(checkNotNull(findViewById<EditText>(R.id.argument)).text.toString())
143       finish()
144     }
145 
146     checkNotNull(findViewById<Button>(R.id.startServiceButton)).setOnClickListener {
147       startService(
148         Intent(this, BackgroundTaskService::class.java).putExtra("usingKeepAlive", false)
149       )
150     }
151 
152     checkNotNull(findViewById<Button>(R.id.startServiceUsingKeepAliveButton)).setOnClickListener {
153       startService(Intent(this, BackgroundTaskService::class.java).putExtra("usingKeepAlive", true))
154     }
155 
156     checkNotNull(findViewById<Button>(R.id.stopServiceButton)).setOnClickListener {
157       stopService(Intent(this, BackgroundTaskService::class.java))
158     }
159   }
160 
onResultnull161   fun onResult(result: String) {
162     checkNotNull(findViewById<TextView>(R.id.status)).text = "Received result: $result"
163   }
164 
onDestroynull165   override fun onDestroy() {
166     super.onDestroy()
167     stopService(Intent(this, BackgroundTaskService::class.java))
168   }
169 }
170 
171 data class ContractArg(
172   val arg: String,
173   val targetPackageName: String,
174   val shouldForward: Boolean = false,
175 )
176 
177 sealed class ColourContract(val colour: Int, private val name: String) :
178   OnboardingActivityApiContract<ContractArg, String>(), IdentifyExecutingContractByAction {
isExecutingnull179   override fun isExecuting(action: String?) = action?.endsWith(".$name") ?: false
180 
181   override fun performCreateIntent(context: Context, arg: ContractArg): Intent =
182     Intent("com.android.onboarding.nodes.testing.testapp.$name").apply {
183       setPackage(arg.targetPackageName)
184       putExtra("KEY", arg.arg)
185       addCategory(CATEGORY_DEFAULT)
186 
187       if (arg.shouldForward) {
188         addFlags(FLAG_ACTIVITY_FORWARD_RESULT)
189       }
190     }
191 
performExtractArgumentnull192   override fun performExtractArgument(intent: Intent): ContractArg =
193     ContractArg(
194       checkNotNull(intent.getStringExtra("KEY")),
195       intent.`package` ?: "",
196       intent.hasFlag(FLAG_ACTIVITY_FORWARD_RESULT),
197     )
198 
199   override fun performParseResult(result: ActivityResult): String =
200     checkNotNull(result.data?.getStringExtra("KEY"))
201 
202   override fun performSetResult(result: String): ActivityResult =
203     ActivityResult(1, Intent().apply { putExtra("KEY", result) })
204 }
205 
206 @OnboardingNode(component = TEST_APP, name = "Red", uiType = OnboardingNode.UiType.OTHER)
207 class RedContract : ColourContract(Color.RED, "red")
208 
209 @OnboardingNode(component = TEST_APP, name = "Blue", uiType = OnboardingNode.UiType.OTHER)
210 class BlueContract : ColourContract(Color.BLUE, "blue")
211 
212 @OnboardingNode(component = TEST_APP, name = "Green", uiType = OnboardingNode.UiType.OTHER)
213 class GreenContract : ColourContract(Color.GREEN, "green")
214 
hasFlagnull215 fun Intent.hasFlag(flag: Int) = (this.flags and flag) == flag
216