xref: /aosp_15_r20/frameworks/base/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * 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 
17 package com.android.systemui.recordissue
18 
19 import android.annotation.SuppressLint
20 import android.content.ContentResolver
21 import android.content.Context
22 import android.database.ContentObserver
23 import android.os.Handler
24 import androidx.annotation.VisibleForTesting
25 import androidx.annotation.WorkerThread
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Background
28 import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
29 import com.android.systemui.res.R
30 import com.android.systemui.settings.UserFileManager
31 import com.android.systemui.settings.UserTracker
32 import com.android.systemui.util.settings.GlobalSettings
33 import com.android.traceur.PresetTraceConfigs
34 import com.android.traceur.TraceConfig
35 import java.util.concurrent.CopyOnWriteArrayList
36 import javax.inject.Inject
37 
38 @SysUISingleton
39 class IssueRecordingState
40 @Inject
41 constructor(
42     private val userTracker: UserTracker,
43     private val userFileManager: UserFileManager,
44     @Background bgHandler: Handler,
45     private val resolver: ContentResolver,
46     private val globalSettings: GlobalSettings,
47 ) {
48 
49     private val prefs
50         get() =
51             userFileManager.getSharedPreferences(
52                 TILE_SPEC,
53                 Context.MODE_PRIVATE,
54                 userTracker.userId,
55             )
56 
57     val customTraceState = CustomTraceState(prefs)
58 
59     var takeBugreport
60         get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false)
61         set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply()
62 
63     var recordScreen
64         get() = prefs.getBoolean(KEY_RECORD_SCREEN, false)
65         set(value) = prefs.edit().putBoolean(KEY_RECORD_SCREEN, value).apply()
66 
67     var hasUserApprovedScreenRecording
68         get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)
69         private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply()
70 
71     // Store the index of the issue type because res ids are generated at compile time and change
72     // in value from one build to another. The index will not change between package versions.
73     private var issueTypeIndex: Int
74         get() = prefs.getInt(KEY_ISSUE_TYPE_INDEX, ISSUE_TYPE_NOT_SET)
75         set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_INDEX, value).apply()
76 
77     var issueTypeRes
78         get() =
79             // If the user has never used the record issue tile, we don't show a default issue type
80             if (issueTypeIndex == ISSUE_TYPE_NOT_SET) ISSUE_TYPE_NOT_SET
81             else ALL_ISSUE_TYPES.keys.toIntArray()[issueTypeIndex]
82         set(value) {
83             issueTypeIndex = ALL_ISSUE_TYPES.keys.toIntArray().indexOf(value)
84         }
85 
86     val traceConfig: TraceConfig
87         get() = ALL_ISSUE_TYPES[issueTypeRes] ?: customTraceState.traceConfig
88 
89     // The 1st part of the title before the ": " is the tag, and the 2nd part is the description
90     var tagTitles: Set<String>
91         get() = prefs.getStringSet(KEY_TAG_TITLES, emptySet()) ?: emptySet()
92         set(value) = prefs.edit().putStringSet(KEY_TAG_TITLES, value).apply()
93 
94     private val listeners = CopyOnWriteArrayList<Runnable>()
95 
96     @VisibleForTesting
97     val onRecordingChangeListener =
98         object : ContentObserver(bgHandler) {
onChangenull99             override fun onChange(selfChange: Boolean) {
100                 isRecording = globalSettings.getInt(KEY_ONGOING_ISSUE_RECORDING, 0) == 1
101                 listeners.forEach(Runnable::run)
102             }
103         }
104 
105     /**
106      * isRecording is purposely always set to false at the initialization of the record issue qs
107      * tile. We want to avoid a situation where the System UI crashed / the device was restarted in
108      * the middle of a trace session and the QS tile is in an active state even though no tracing is
109      * ongoing.
110      */
111     var isRecording = false
112         @WorkerThread
113         set(value) {
114             globalSettings.putInt(KEY_ONGOING_ISSUE_RECORDING, if (value) 1 else 0)
115             field = value
116         }
117 
markUserApprovalForScreenRecordingnull118     fun markUserApprovalForScreenRecording() {
119         hasUserApprovedScreenRecording = true
120     }
121 
122     @WorkerThread
123     @SuppressLint("RegisterContentObserverViaContentResolver")
addListenernull124     fun addListener(listener: Runnable) {
125         if (listeners.isEmpty()) {
126             resolver.registerContentObserver(
127                 globalSettings.getUriFor(KEY_ONGOING_ISSUE_RECORDING),
128                 false,
129                 onRecordingChangeListener,
130             )
131         }
132         listeners.add(listener)
133     }
134 
135     @WorkerThread
136     @SuppressLint("RegisterContentObserverViaContentResolver")
removeListenernull137     fun removeListener(listener: Runnable) {
138         listeners.remove(listener)
139         if (listeners.isEmpty()) {
140             resolver.unregisterContentObserver(onRecordingChangeListener)
141         }
142     }
143 
144     companion object {
145         private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport"
146         private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
147         private const val KEY_RECORD_SCREEN = "key_recordScreen"
148         private const val KEY_TAG_TITLES = "key_tagTitles"
149         private const val KEY_ONGOING_ISSUE_RECORDING = "issueRecordingOngoing"
150         const val KEY_ISSUE_TYPE_INDEX = "key_issueTypeIndex"
151         const val ISSUE_TYPE_NOT_SET = -1
152         const val TAG_TITLE_DELIMITER = ": "
153 
154         val ALL_ISSUE_TYPES: LinkedHashMap<Int, TraceConfig?> =
155             linkedMapOf(
156                 Pair(R.string.performance, PresetTraceConfigs.getPerformanceConfig()),
157                 Pair(R.string.user_interface, PresetTraceConfigs.getUiConfig()),
158                 Pair(R.string.battery, PresetTraceConfigs.getBatteryConfig()),
159                 Pair(R.string.thermal, PresetTraceConfigs.getThermalConfig()),
160                 Pair(R.string.custom, null), // Null means we are using a custom trace config
161             )
162     }
163 }
164