• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.util;
18 
19 import 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 public class StateSet {
38     /**
39      * The order here is very important to
40      * {@link android.view.View#getDrawableState()}
41      */
42     private static final int[][] VIEW_STATE_SETS;
43 
44     /** @hide */
45     public static final int VIEW_STATE_WINDOW_FOCUSED = 1;
46     /** @hide */
47     public static final int VIEW_STATE_SELECTED = 1 << 1;
48     /** @hide */
49     public static final int VIEW_STATE_FOCUSED = 1 << 2;
50     /** @hide */
51     public static final int VIEW_STATE_ENABLED = 1 << 3;
52     /** @hide */
53     public static final int VIEW_STATE_PRESSED = 1 << 4;
54     /** @hide */
55     public static final int VIEW_STATE_ACTIVATED = 1 << 5;
56     /** @hide */
57     public static final int VIEW_STATE_ACCELERATED = 1 << 6;
58     /** @hide */
59     public static final int VIEW_STATE_HOVERED = 1 << 7;
60     /** @hide */
61     public static final int VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
62     /** @hide */
63     public static final int VIEW_STATE_DRAG_HOVERED = 1 << 9;
64 
65     static final int[] VIEW_STATE_IDS = new int[] {
66             R.attr.state_window_focused,    VIEW_STATE_WINDOW_FOCUSED,
67             R.attr.state_selected,          VIEW_STATE_SELECTED,
68             R.attr.state_focused,           VIEW_STATE_FOCUSED,
69             R.attr.state_enabled,           VIEW_STATE_ENABLED,
70             R.attr.state_pressed,           VIEW_STATE_PRESSED,
71             R.attr.state_activated,         VIEW_STATE_ACTIVATED,
72             R.attr.state_accelerated,       VIEW_STATE_ACCELERATED,
73             R.attr.state_hovered,           VIEW_STATE_HOVERED,
74             R.attr.state_drag_can_accept,   VIEW_STATE_DRAG_CAN_ACCEPT,
75             R.attr.state_drag_hovered,      VIEW_STATE_DRAG_HOVERED
76     };
77 
78     static {
79         if ((VIEW_STATE_IDS.length / 2) != R.styleable.ViewDrawableStates.length) {
80             throw new IllegalStateException(
81                     "VIEW_STATE_IDs array length does not match ViewDrawableStates style array");
82         }
83 
84         final int[] orderedIds = new int[VIEW_STATE_IDS.length];
85         for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
86             final int viewState = R.styleable.ViewDrawableStates[i];
87             for (int j = 0; j < VIEW_STATE_IDS.length; j += 2) {
88                 if (VIEW_STATE_IDS[j] == viewState) {
89                     orderedIds[i * 2] = viewState;
90                     orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
91                 }
92             }
93         }
94 
95         final int NUM_BITS = VIEW_STATE_IDS.length / 2;
96         VIEW_STATE_SETS = new int[1 << NUM_BITS][];
97         for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
98             final int numBits = Integer.bitCount(i);
99             final int[] set = new int[numBits];
100             int pos = 0;
101             for (int j = 0; j < orderedIds.length; j += 2) {
102                 if ((i & orderedIds[j + 1]) != 0) {
103                     set[pos++] = orderedIds[j];
104                 }
105             }
106             VIEW_STATE_SETS[i] = set;
107         }
108     }
109 
110     /** @hide */
get(int mask)111     public static int[] get(int mask) {
112         if (mask >= VIEW_STATE_SETS.length) {
113             throw new IllegalArgumentException("Invalid state set mask");
114         }
115         return VIEW_STATE_SETS[mask];
116     }
117 
118     /** @hide */
StateSet()119     public StateSet() {}
120 
121     /**
122      * A state specification that will be matched by all StateSets.
123      */
124     public static final int[] WILD_CARD = new int[0];
125 
126     /**
127      * A state set that does not contain any valid states.
128      */
129     public static final int[] NOTHING = new int[] { 0 };
130 
131     /**
132      * Return whether the stateSetOrSpec is matched by all StateSets.
133      *
134      * @param stateSetOrSpec a state set or state spec.
135      */
isWildCard(int[] stateSetOrSpec)136     public static boolean isWildCard(int[] stateSetOrSpec) {
137         return stateSetOrSpec.length == 0 || stateSetOrSpec[0] == 0;
138     }
139 
140     /**
141      * Return whether the stateSet matches the desired stateSpec.
142      *
143      * @param stateSpec an array of required (if positive) or
144      *        prohibited (if negative) {@link android.view.View} states.
145      * @param stateSet an array of {@link android.view.View} states
146      */
stateSetMatches(int[] stateSpec, int[] stateSet)147     public static boolean stateSetMatches(int[] stateSpec, int[] stateSet) {
148         if (stateSet == null) {
149             return (stateSpec == null || isWildCard(stateSpec));
150         }
151         int stateSpecSize = stateSpec.length;
152         int stateSetSize = stateSet.length;
153         for (int i = 0; i < stateSpecSize; i++) {
154             int stateSpecState = stateSpec[i];
155             if (stateSpecState == 0) {
156                 // We've reached the end of the cases to match against.
157                 return true;
158             }
159             final boolean mustMatch;
160             if (stateSpecState > 0) {
161                 mustMatch = true;
162             } else {
163                 // We use negative values to indicate must-NOT-match states.
164                 mustMatch = false;
165                 stateSpecState = -stateSpecState;
166             }
167             boolean found = false;
168             for (int j = 0; j < stateSetSize; j++) {
169                 final int state = stateSet[j];
170                 if (state == 0) {
171                     // We've reached the end of states to match.
172                     if (mustMatch) {
173                         // We didn't find this must-match state.
174                         return false;
175                     } else {
176                         // Continue checking other must-not-match states.
177                         break;
178                     }
179                 }
180                 if (state == stateSpecState) {
181                     if (mustMatch) {
182                         found = true;
183                         // Continue checking other other must-match states.
184                         break;
185                     } else {
186                         // Any match of a must-not-match state returns false.
187                         return false;
188                     }
189                 }
190             }
191             if (mustMatch && !found) {
192                 // We've reached the end of states to match and we didn't
193                 // find a must-match state.
194                 return false;
195             }
196         }
197         return true;
198     }
199 
200     /**
201      * Return whether the state matches the desired stateSpec.
202      *
203      * @param stateSpec an array of required (if positive) or
204      *        prohibited (if negative) {@link android.view.View} states.
205      * @param state a {@link android.view.View} state
206      */
stateSetMatches(int[] stateSpec, int state)207     public static boolean stateSetMatches(int[] stateSpec, int state) {
208         int stateSpecSize = stateSpec.length;
209         for (int i = 0; i < stateSpecSize; i++) {
210             int stateSpecState = stateSpec[i];
211             if (stateSpecState == 0) {
212                 // We've reached the end of the cases to match against.
213                 return true;
214             }
215             if (stateSpecState > 0) {
216                 if (state != stateSpecState) {
217                    return false;
218                 }
219             } else {
220                 // We use negative values to indicate must-NOT-match states.
221                 if (state == -stateSpecState) {
222                     // We matched a must-not-match case.
223                     return false;
224                 }
225             }
226         }
227         return true;
228     }
229 
230     /**
231      * Check whether a list of state specs has an attribute specified.
232      * @param stateSpecs a list of state specs we're checking.
233      * @param attr an attribute we're looking for.
234      * @return {@code true} if the attribute is contained in the state specs.
235      * @hide
236      */
containsAttribute(int[][] stateSpecs, int attr)237     public static boolean containsAttribute(int[][] stateSpecs, int attr) {
238         if (stateSpecs != null) {
239             for (int[] spec : stateSpecs) {
240                 if (spec == null) {
241                     break;
242                 }
243                 for (int specAttr : spec) {
244                     if (specAttr == attr || -specAttr == attr) {
245                         return true;
246                     }
247                 }
248             }
249         }
250         return false;
251     }
252 
trimStateSet(int[] states, int newSize)253     public static int[] trimStateSet(int[] states, int newSize) {
254         if (states.length == newSize) {
255             return states;
256         }
257 
258         int[] trimmedStates = new int[newSize];
259         System.arraycopy(states, 0, trimmedStates, 0, newSize);
260         return trimmedStates;
261     }
262 
dump(int[] states)263     public static String dump(int[] states) {
264         StringBuilder sb = new StringBuilder();
265 
266         int count = states.length;
267         for (int i = 0; i < count; i++) {
268 
269             switch (states[i]) {
270             case R.attr.state_window_focused:
271                 sb.append("W ");
272                 break;
273             case R.attr.state_pressed:
274                 sb.append("P ");
275                 break;
276             case R.attr.state_selected:
277                 sb.append("S ");
278                 break;
279             case R.attr.state_focused:
280                 sb.append("F ");
281                 break;
282             case R.attr.state_enabled:
283                 sb.append("E ");
284                 break;
285             case R.attr.state_checked:
286                 sb.append("C ");
287                 break;
288             case R.attr.state_activated:
289                 sb.append("A ");
290                 break;
291             }
292         }
293 
294         return sb.toString();
295     }
296 }
297