blob: 4bbc0f86360317fe1765f2dfcd52fa8d014d41d0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package android.util;
18
19import com.android.internal.R;
20
21/**
22 * State sets are arrays of positive ints where each element
23 * represents the state of a {@link android.view.View} (e.g. focused,
24 * selected, visible, etc.). A {@link android.view.View} may be in
25 * one or more of those states.
26 *
27 * A state spec is an array of signed ints where each element
28 * represents a required (if positive) or an undesired (if negative)
29 * {@link android.view.View} state.
30 *
31 * Utils dealing with state sets.
32 *
33 * In theory we could encapsulate the state set and state spec arrays
34 * and not have static methods here but there is some concern about
35 * performance since these methods are called during view drawing.
36 */
37
38public class StateSet {
Alan Viverettec5b95c22015-01-07 13:57:12 -080039 /**
40 * The order here is very important to
41 * {@link android.view.View#getDrawableState()}
42 */
43 private static final int[][] VIEW_STATE_SETS;
44
45 /** @hide */
46 public static final int VIEW_STATE_WINDOW_FOCUSED = 1;
47 /** @hide */
48 public static final int VIEW_STATE_SELECTED = 1 << 1;
49 /** @hide */
50 public static final int VIEW_STATE_FOCUSED = 1 << 2;
51 /** @hide */
52 public static final int VIEW_STATE_ENABLED = 1 << 3;
53 /** @hide */
54 public static final int VIEW_STATE_PRESSED = 1 << 4;
55 /** @hide */
56 public static final int VIEW_STATE_ACTIVATED = 1 << 5;
57 /** @hide */
58 public static final int VIEW_STATE_ACCELERATED = 1 << 6;
59 /** @hide */
60 public static final int VIEW_STATE_HOVERED = 1 << 7;
61 /** @hide */
62 public static final int VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
63 /** @hide */
64 public static final int VIEW_STATE_DRAG_HOVERED = 1 << 9;
65
66 static final int[] VIEW_STATE_IDS = new int[] {
67 R.attr.state_window_focused, VIEW_STATE_WINDOW_FOCUSED,
68 R.attr.state_selected, VIEW_STATE_SELECTED,
69 R.attr.state_focused, VIEW_STATE_FOCUSED,
70 R.attr.state_enabled, VIEW_STATE_ENABLED,
71 R.attr.state_pressed, VIEW_STATE_PRESSED,
72 R.attr.state_activated, VIEW_STATE_ACTIVATED,
73 R.attr.state_accelerated, VIEW_STATE_ACCELERATED,
74 R.attr.state_hovered, VIEW_STATE_HOVERED,
75 R.attr.state_drag_can_accept, VIEW_STATE_DRAG_CAN_ACCEPT,
76 R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED
77 };
78
79 static {
80 if ((VIEW_STATE_IDS.length / 2) != R.styleable.ViewDrawableStates.length) {
81 throw new IllegalStateException(
82 "VIEW_STATE_IDs array length does not match ViewDrawableStates style array");
83 }
84
85 final int[] orderedIds = new int[VIEW_STATE_IDS.length];
86 for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
87 final int viewState = R.styleable.ViewDrawableStates[i];
88 for (int j = 0; j < VIEW_STATE_IDS.length; j += 2) {
89 if (VIEW_STATE_IDS[j] == viewState) {
90 orderedIds[i * 2] = viewState;
91 orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
92 }
93 }
94 }
95
96 final int NUM_BITS = VIEW_STATE_IDS.length / 2;
97 VIEW_STATE_SETS = new int[1 << NUM_BITS][];
98 for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
99 final int numBits = Integer.bitCount(i);
100 final int[] set = new int[numBits];
101 int pos = 0;
102 for (int j = 0; j < orderedIds.length; j += 2) {
103 if ((i & orderedIds[j + 1]) != 0) {
104 set[pos++] = orderedIds[j];
105 }
106 }
107 VIEW_STATE_SETS[i] = set;
108 }
109 }
110
111 /** @hide */
112 public static int[] get(int mask) {
113 if (mask >= VIEW_STATE_SETS.length) {
114 throw new IllegalArgumentException("Invalid state set mask");
115 }
116 return VIEW_STATE_SETS[mask];
117 }
118
119 /** @hide */
120 public StateSet() {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
Jeff Brown57bf3d92015-05-29 19:11:05 -0700122 /**
123 * A state specification that will be matched by all StateSets.
124 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public static final int[] WILD_CARD = new int[0];
Jeff Brown57bf3d92015-05-29 19:11:05 -0700126
127 /**
128 * A state set that does not contain any valid states.
129 */
Dianne Hackborn079e2352010-10-18 17:02:43 -0700130 public static final int[] NOTHING = new int[] { 0 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
132 /**
133 * Return whether the stateSetOrSpec is matched by all StateSets.
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800134 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 * @param stateSetOrSpec a state set or state spec.
136 */
137 public static boolean isWildCard(int[] stateSetOrSpec) {
138 return stateSetOrSpec.length == 0 || stateSetOrSpec[0] == 0;
139 }
140
141 /**
142 * Return whether the stateSet matches the desired stateSpec.
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800143 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 * @param stateSpec an array of required (if positive) or
145 * prohibited (if negative) {@link android.view.View} states.
146 * @param stateSet an array of {@link android.view.View} states
147 */
148 public static boolean stateSetMatches(int[] stateSpec, int[] stateSet) {
149 if (stateSet == null) {
150 return (stateSpec == null || isWildCard(stateSpec));
151 }
152 int stateSpecSize = stateSpec.length;
153 int stateSetSize = stateSet.length;
154 for (int i = 0; i < stateSpecSize; i++) {
155 int stateSpecState = stateSpec[i];
156 if (stateSpecState == 0) {
157 // We've reached the end of the cases to match against.
158 return true;
159 }
160 final boolean mustMatch;
161 if (stateSpecState > 0) {
162 mustMatch = true;
163 } else {
164 // We use negative values to indicate must-NOT-match states.
165 mustMatch = false;
166 stateSpecState = -stateSpecState;
167 }
168 boolean found = false;
169 for (int j = 0; j < stateSetSize; j++) {
170 final int state = stateSet[j];
171 if (state == 0) {
172 // We've reached the end of states to match.
173 if (mustMatch) {
174 // We didn't find this must-match state.
175 return false;
176 } else {
177 // Continue checking other must-not-match states.
178 break;
179 }
180 }
181 if (state == stateSpecState) {
182 if (mustMatch) {
183 found = true;
184 // Continue checking other other must-match states.
185 break;
186 } else {
187 // Any match of a must-not-match state returns false.
188 return false;
189 }
190 }
191 }
192 if (mustMatch && !found) {
193 // We've reached the end of states to match and we didn't
194 // find a must-match state.
195 return false;
196 }
197 }
198 return true;
199 }
200
201 /**
202 * Return whether the state matches the desired stateSpec.
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800203 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 * @param stateSpec an array of required (if positive) or
205 * prohibited (if negative) {@link android.view.View} states.
206 * @param state a {@link android.view.View} state
207 */
208 public static boolean stateSetMatches(int[] stateSpec, int state) {
209 int stateSpecSize = stateSpec.length;
210 for (int i = 0; i < stateSpecSize; i++) {
211 int stateSpecState = stateSpec[i];
212 if (stateSpecState == 0) {
213 // We've reached the end of the cases to match against.
214 return true;
215 }
216 if (stateSpecState > 0) {
217 if (state != stateSpecState) {
218 return false;
219 }
220 } else {
221 // We use negative values to indicate must-NOT-match states.
222 if (state == -stateSpecState) {
223 // We matched a must-not-match case.
224 return false;
225 }
226 }
227 }
228 return true;
229 }
230
Jiaquan Hef3800722017-02-02 14:26:45 -0800231 /**
232 * Check whether a list of state specs has an attribute specified.
233 * @param stateSpecs a list of state specs we're checking.
234 * @param attr an attribute we're looking for.
235 * @return {@code true} if the attribute is contained in the state specs.
236 * @hide
237 */
238 public static boolean containsAttribute(int[][] stateSpecs, int attr) {
239 if (stateSpecs != null) {
240 for (int[] spec : stateSpecs) {
241 if (spec == null) {
242 break;
243 }
244 for (int specAttr : spec) {
245 if (specAttr == attr || -specAttr == attr) {
246 return true;
247 }
248 }
249 }
250 }
251 return false;
252 }
253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 public static int[] trimStateSet(int[] states, int newSize) {
255 if (states.length == newSize) {
256 return states;
257 }
258
259 int[] trimmedStates = new int[newSize];
260 System.arraycopy(states, 0, trimmedStates, 0, newSize);
261 return trimmedStates;
262 }
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 public static String dump(int[] states) {
265 StringBuilder sb = new StringBuilder();
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 int count = states.length;
268 for (int i = 0; i < count; i++) {
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 switch (states[i]) {
271 case R.attr.state_window_focused:
272 sb.append("W ");
273 break;
274 case R.attr.state_pressed:
275 sb.append("P ");
276 break;
277 case R.attr.state_selected:
278 sb.append("S ");
279 break;
280 case R.attr.state_focused:
281 sb.append("F ");
282 break;
283 case R.attr.state_enabled:
284 sb.append("E ");
285 break;
Alan Viverette189d4f52015-12-16 16:06:23 -0500286 case R.attr.state_checked:
287 sb.append("C ");
288 break;
289 case R.attr.state_activated:
290 sb.append("A ");
291 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
293 }
Jesse Wilsona0f8bc52011-02-24 10:44:33 -0800294
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 return sb.toString();
296 }
297}