• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.android.car.rotary;
17 
18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
19 
20 import android.view.accessibility.AccessibilityNodeInfo;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 import androidx.annotation.VisibleForTesting;
25 
26 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
27 import com.android.internal.util.dump.DualDumpOutputStream;
28 
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.Stack;
32 
33 /** Cache of window type and most recently focused node for each window ID. */
34 class WindowCache {
35     /** Window IDs. */
36     private final Stack<Integer> mWindowIds;
37     /** Window types keyed by window IDs. */
38     private final Map<Integer, Integer> mWindowTypes;
39     /** Most recent focused nodes keyed by window IDs. */
40     private final Map<Integer, AccessibilityNodeInfo> mFocusedNodes;
41 
42     @NonNull
43     private NodeCopier mNodeCopier = new NodeCopier();
44 
WindowCache()45     WindowCache() {
46         mWindowIds = new Stack<>();
47         mWindowTypes = new HashMap<>();
48         mFocusedNodes = new HashMap<>();
49     }
50 
51     /**
52      * Saves the focused node of the given window, removing the existing entry, if any. This method
53      * should be called when the focused node changed.
54      */
saveFocusedNode(int windowId, @NonNull AccessibilityNodeInfo focusedNode)55     void saveFocusedNode(int windowId, @NonNull AccessibilityNodeInfo focusedNode) {
56         if (mFocusedNodes.containsKey(windowId)) {
57             // Call remove(Integer) to remove.
58             AccessibilityNodeInfo oldNode = mFocusedNodes.remove(windowId);
59             oldNode.recycle();
60         }
61         mFocusedNodes.put(windowId, copyNode(focusedNode));
62     }
63 
64     /**
65      * Saves the type of the given window, removing the existing entry, if any. This method should
66      * be called when a window was just added.
67      */
saveWindowType(int windowId, int windowType)68     void saveWindowType(int windowId, int windowType) {
69         Integer id = windowId;
70         if (mWindowIds.contains(id)) {
71             // Call remove(Integer) to remove.
72             mWindowIds.remove(id);
73         }
74         mWindowIds.push(id);
75 
76         mWindowTypes.put(windowId, windowType);
77     }
78 
79     /**
80      * Removes an entry if it exists. This method should be called when a window was just removed.
81      */
remove(int windowId)82     void remove(int windowId) {
83         Integer id = windowId;
84         if (mWindowIds.contains(id)) {
85             // Call remove(Integer) to remove.
86             mWindowIds.remove(id);
87             mWindowTypes.remove(id);
88             AccessibilityNodeInfo node = mFocusedNodes.remove(id);
89             Utils.recycleNode(node);
90         }
91     }
92 
93     /** Gets the window type keyed by {@code windowId}, or null if none. */
94     @Nullable
getWindowType(int windowId)95     Integer getWindowType(int windowId) {
96         return mWindowTypes.get(windowId);
97     }
98 
99     /**
100      * Returns a copy of the most recently focused node in the most recently added window, or null
101      * if none.
102      */
103     @Nullable
getMostRecentFocusedNode()104     AccessibilityNodeInfo getMostRecentFocusedNode() {
105         if (mWindowIds.isEmpty()) {
106             return null;
107         }
108         Integer recentWindowId = mWindowIds.peek();
109         if (recentWindowId == null) {
110             return null;
111         }
112         return copyNode(mFocusedNodes.get(recentWindowId));
113     }
114 
115     /** Sets a mock NodeCopier instance for testing. */
116     @VisibleForTesting
setNodeCopier(@onNull NodeCopier nodeCopier)117     void setNodeCopier(@NonNull NodeCopier nodeCopier) {
118         mNodeCopier = nodeCopier;
119     }
120 
copyNode(@ullable AccessibilityNodeInfo node)121     private AccessibilityNodeInfo copyNode(@Nullable AccessibilityNodeInfo node) {
122         return mNodeCopier.copy(node);
123     }
124 
125     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId)126     void dump(@NonNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto,
127             @NonNull String fieldName, long fieldId) {
128         long fieldToken = dumpOutputStream.start(fieldName, fieldId);
129         DumpUtils.writeIntegers(dumpOutputStream, dumpAsProto, "windowIds",
130                 RotaryProtos.WindowCache.WINDOW_IDS, mWindowIds);
131         DumpUtils.writeWindowTypes(dumpOutputStream, dumpAsProto, "windowTypes",
132                 RotaryProtos.WindowCache.WINDOW_TYPES, mWindowTypes);
133         DumpUtils.writeFocusedNodes(dumpOutputStream, dumpAsProto, "focusedNodes",
134                 RotaryProtos.WindowCache.FOCUSED_NODES, mFocusedNodes);
135         dumpOutputStream.end(fieldToken);
136     }
137 
138 }
139