blob: d1ebc5ff25ff33c1a52be1cccb14e1709cf38785 [file] [log] [blame]
Adam Lesinski35168002014-07-21 15:25:30 -07001/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16package android.app.usage;
17
18import android.content.ComponentName;
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.Arrays;
23import java.util.List;
24
25/**
26 * A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
27 * from which to read {@link android.app.usage.UsageEvents.Event} objects.
28 */
29public final class UsageEvents implements Parcelable {
30
31 /**
32 * An event representing a state change for a component.
33 */
34 public static final class Event {
35
36 /**
37 * No event type.
38 */
39 public static final int NONE = 0;
40
41 /**
42 * An event type denoting that a component moved to the foreground.
43 */
44 public static final int MOVE_TO_FOREGROUND = 1;
45
46 /**
47 * An event type denoting that a component moved to the background.
48 */
49 public static final int MOVE_TO_BACKGROUND = 2;
50
51 /**
52 * An event type denoting that a component was in the foreground when the stats
53 * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
54 * {@hide}
55 */
56 public static final int END_OF_DAY = 3;
57
58 /**
59 * An event type denoting that a component was in the foreground the previous day.
60 * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
61 * {@hide}
62 */
63 public static final int CONTINUE_PREVIOUS_DAY = 4;
64
65 /**
66 * {@hide}
67 */
68 public ComponentName mComponent;
69
70 /**
71 * {@hide}
72 */
73 public long mTimeStamp;
74
75 /**
76 * {@hide}
77 */
78 public int mEventType;
79
80 /**
81 * The component this event represents.
82 */
83 public ComponentName getComponent() {
84 return mComponent;
85 }
86
87 /**
88 * The time at which this event occurred.
89 */
90 public long getTimeStamp() {
91 return mTimeStamp;
92 }
93
94 /**
95 * The event type.
96 *
97 * See {@link #MOVE_TO_BACKGROUND}
98 * See {@link #MOVE_TO_FOREGROUND}
99 */
100 public int getEventType() {
101 return mEventType;
102 }
103 }
104
105 // Only used when creating the resulting events. Not used for reading/unparceling.
106 private List<Event> mEventsToWrite = null;
107
108 // Only used for reading/unparceling events.
109 private Parcel mParcel = null;
110 private final int mEventCount;
111
112 private int mIndex = 0;
113
114 /*
115 * In order to save space, since ComponentNames will be duplicated everywhere,
116 * we use a map and index into it.
117 */
118 private ComponentName[] mComponentNameTable;
119
120 /**
121 * Construct the iterator from a parcel.
122 * {@hide}
123 */
124 public UsageEvents(Parcel in) {
125 mEventCount = in.readInt();
126 mIndex = in.readInt();
127 if (mEventCount > 0) {
128 mComponentNameTable = in.createTypedArray(ComponentName.CREATOR);
129
130 final int listByteLength = in.readInt();
131 final int positionInParcel = in.readInt();
132 mParcel = Parcel.obtain();
133 mParcel.setDataPosition(0);
134 mParcel.appendFrom(in, in.dataPosition(), listByteLength);
135 mParcel.setDataSize(mParcel.dataPosition());
136 mParcel.setDataPosition(positionInParcel);
137 }
138 }
139
140 /**
141 * Create an empty iterator.
142 * {@hide}
143 */
144 UsageEvents() {
145 mEventCount = 0;
146 }
147
148 /**
149 * Construct the iterator in preparation for writing it to a parcel.
150 * {@hide}
151 */
152 public UsageEvents(List<Event> events, ComponentName[] nameTable) {
153 mComponentNameTable = nameTable;
154 mEventCount = events.size();
155 mEventsToWrite = events;
156 }
157
158 /**
159 * Returns whether or not there are more events to read using
160 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
161 *
162 * @return true if there are more events, false otherwise.
163 */
164 public boolean hasNextEvent() {
165 return mIndex < mEventCount;
166 }
167
168 /**
169 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
170 * resulting data into {@code eventOut}.
171 *
172 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
173 * next event data.
174 * @return true if an event was available, false if there are no more events.
175 */
176 public boolean getNextEvent(Event eventOut) {
177 if (mIndex >= mEventCount) {
178 return false;
179 }
180
181 final int index = mParcel.readInt();
182 eventOut.mComponent = mComponentNameTable[index];
183 eventOut.mEventType = mParcel.readInt();
184 eventOut.mTimeStamp = mParcel.readLong();
185 mIndex++;
186
187 if (mIndex >= mEventCount) {
188 mParcel.recycle();
189 mParcel = null;
190 }
191 return true;
192 }
193
194 /**
195 * Resets the collection so that it can be iterated over from the beginning.
196 */
197 public void resetToStart() {
198 mIndex = 0;
199 if (mParcel != null) {
200 mParcel.setDataPosition(0);
201 }
202 }
203
204 @Override
205 public int describeContents() {
206 return 0;
207 }
208
209 @Override
210 public void writeToParcel(Parcel dest, int flags) {
211 dest.writeInt(mEventCount);
212 dest.writeInt(mIndex);
213 if (mEventCount > 0) {
214 dest.writeTypedArray(mComponentNameTable, flags);
215
216 if (mEventsToWrite != null) {
217 // Write out the events
218 Parcel p = Parcel.obtain();
219 try {
220 p.setDataPosition(0);
221 for (int i = 0; i < mEventCount; i++) {
222 final Event event = mEventsToWrite.get(i);
223
224 int index = Arrays.binarySearch(mComponentNameTable, event.getComponent());
225 if (index < 0) {
226 throw new IllegalStateException(event.getComponent().toShortString() +
227 " is not in the component name table");
228 }
229 p.writeInt(index);
230 p.writeInt(event.getEventType());
231 p.writeLong(event.getTimeStamp());
232 }
233 final int listByteLength = p.dataPosition();
234
235 // Write the total length of the data.
236 dest.writeInt(listByteLength);
237
238 // Write our current position into the data.
239 dest.writeInt(0);
240
241 // Write the data.
242 dest.appendFrom(p, 0, listByteLength);
243 } finally {
244 p.recycle();
245 }
246
247 } else if (mParcel != null) {
248 // Write the total length of the data.
249 dest.writeInt(mParcel.dataSize());
250
251 // Write out current position into the data.
252 dest.writeInt(mParcel.dataPosition());
253
254 // Write the data.
255 dest.appendFrom(mParcel, 0, mParcel.dataSize());
256 } else {
257 throw new IllegalStateException(
258 "Either mParcel or mEventsToWrite must not be null");
259 }
260 }
261 }
262
263 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
264 @Override
265 public UsageEvents createFromParcel(Parcel source) {
266 return new UsageEvents(source);
267 }
268
269 @Override
270 public UsageEvents[] newArray(int size) {
271 return new UsageEvents[size];
272 }
273 };
274
275 @Override
276 protected void finalize() throws Throwable {
277 super.finalize();
278 if (mParcel != null) {
279 mParcel.recycle();
280 mParcel = null;
281 }
282 }
283}