1 /*
<lambda>null2  * Copyright (C) 2024 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.example.tracing.demo
17 
18 import android.app.Activity
19 import android.os.Bundle
20 import android.os.Trace
21 import android.view.LayoutInflater
22 import android.view.View
23 import android.view.ViewGroup
24 import android.widget.Button
25 import android.widget.LinearLayout
26 import android.widget.ScrollView
27 import android.widget.TextView
28 import com.android.app.tracing.coroutines.createCoroutineTracingContext
29 import com.example.tracing.demo.experiments.Experiment
30 import com.example.tracing.demo.experiments.TRACK_NAME
31 import kotlin.coroutines.cancellation.CancellationException
32 import kotlin.random.Random
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.Dispatchers
35 import kotlinx.coroutines.Job
36 import kotlinx.coroutines.launch
37 
38 class MainActivity : Activity() {
39 
40     private val allExperiments by lazy {
41         (applicationContext as MainApplication).appComponent.getAllExperiments()
42     }
43 
44     val mainScope: CoroutineScope =
45         CoroutineScope(
46             Dispatchers.Main +
47                 createCoroutineTracingContext("test-scope", walkStackForDefaultNames = true)
48         )
49 
50     private var logContainer: ScrollView? = null
51     private var loggerView: TextView? = null
52 
53     private fun <T : Experiment> connectButtonsForExperiment(demo: T, view: ViewGroup) {
54         val className = demo::class.simpleName
55         view.findViewById<TextView>(R.id.description).text =
56             baseContext.getString(R.string.run_experiment_button_text, className, demo.description)
57         val currentState = view.findViewById<TextView>(R.id.current_state)
58 
59         val launchedJobs = mutableListOf<Job>()
60 
61         view.findViewById<Button>(R.id.start_button).setOnClickListener {
62             val cookie = Random.nextInt()
63             Trace.asyncTraceForTrackBegin(
64                 Trace.TRACE_TAG_APP,
65                 TRACK_NAME,
66                 "Running: $className",
67                 cookie,
68             )
69 
70             val job = mainScope.launch { demo.start() }
71             job.invokeOnCompletion { cause ->
72                 Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, cookie)
73                 val message =
74                     when (cause) {
75                         null -> "completed normally"
76                         is CancellationException -> "cancelled normally"
77                         else -> "failed"
78                     }
79                 mainExecutor.execute {
80                     currentState.text = message
81                     appendLine("$className $message")
82                 }
83             }
84 
85             launchedJobs.add(job)
86 
87             currentState.text = "started"
88             appendLine("$className started")
89         }
90 
91         view.findViewById<Button>(R.id.cancel_button).setOnClickListener {
92             var activeJobs = 0
93             launchedJobs.forEach {
94                 if (it.isActive) activeJobs++
95                 it.cancel()
96             }
97             appendLine(if (activeJobs == 0) "Nothing to cancel." else "Cancelled $activeJobs jobs.")
98             launchedJobs.clear()
99         }
100     }
101 
102     override fun onCreate(savedInstanceState: Bundle?) {
103         super.onCreate(savedInstanceState)
104         setContentView(R.layout.activity_main)
105         logContainer = requireViewById(R.id.log_container)
106         loggerView = requireViewById(R.id.logger_view)
107         val experimentList = requireViewById<LinearLayout>(R.id.experiment_list)
108         val inflater = LayoutInflater.from(baseContext)
109         allExperiments.forEach {
110             val experimentButtons =
111                 inflater.inflate(R.layout.experiment_buttons, experimentList, false) as ViewGroup
112             connectButtonsForExperiment(it.value.get(), experimentButtons)
113             experimentList.addView(experimentButtons)
114         }
115     }
116 
117     private fun appendLine(message: String) {
118         loggerView?.append("$message\n")
119         logContainer?.fullScroll(View.FOCUS_DOWN)
120     }
121 }
122