1 /*
2  * Copyright (C) 2023 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.tools.traces.events
18 
19 import android.tools.Timestamp
20 
21 /**
22  * An Event from the [EventLog] representing a window focus change or request.
23  *
24  * @param timestamp The wall clock time in nanoseconds when the entry was written.
25  * @param window The window that is the target of the focus event.
26  * @param type The type of focus event this is.
27  * @param reason The reason for the focus event.
28  * @param processId The process ID which wrote the log entry
29  * @param uid The UID which wrote the log entry, special UIDs are strings instead of numbers (e.g.
30  *   root)
31  * @param threadId The thread which wrote the log entry
32  * @param tag The type tag code of the entry
33  */
34 class FocusEvent(
35     timestamp: Timestamp,
36     val window: String,
37     val type: Type,
38     val reason: String,
39     processId: Int,
40     uid: String,
41     threadId: Int
42 ) : Event(timestamp, processId, uid, threadId, INPUT_FOCUS_TAG) {
43     enum class Type {
44         GAINED,
45         LOST,
46         REQUESTED
47     }
48 
toStringnull49     override fun toString(): String {
50         return "$timestamp: Focus ${type.name} $window Reason=$reason"
51     }
52 
hasFocusnull53     fun hasFocus(): Boolean {
54         return this.type == Type.GAINED
55     }
56 
57     companion object {
fromnull58         fun from(
59             timestamp: Timestamp,
60             processId: Int,
61             uid: String,
62             threadId: Int,
63             data: Collection<String>
64         ) =
65             FocusEvent(
66                 timestamp,
67                 getWindowFromData(data.first()),
68                 getFocusFromData(data.first()),
69                 data.drop(1).first().removePrefix("reason="),
70                 processId,
71                 uid,
72                 threadId
73             )
74 
75         private fun getWindowFromData(data: String): String {
76             var expectedWhiteSpace = 2
77             return data.dropWhile { !it.isWhitespace() || --expectedWhiteSpace > 0 }.drop(1)
78         }
79 
getFocusFromDatanull80         private fun getFocusFromData(data: String): Type {
81             return when {
82                 data.contains(" entering ") -> Type.GAINED
83                 data.contains(" leaving ") -> Type.LOST
84                 else -> Type.REQUESTED
85             }
86         }
87 
88         const val INPUT_FOCUS_TAG = "input_focus"
89     }
90 }
91