All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowUsageStatsManager Maven / Gradle / Ivy

The newest version!
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.usage.BroadcastResponseStats;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageStatsManager.UsageSource;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.util.ArraySet;
import com.google.auto.value.AutoValue;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Range;
import com.google.common.collect.SetMultimap;
import com.google.common.primitives.Ints;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.TimeUnit;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.ClassName;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;
import org.robolectric.versioning.AndroidVersions.V;

/** Shadow of {@link UsageStatsManager}. */
@Implements(value = UsageStatsManager.class)
public class ShadowUsageStatsManager {
  private static @StandbyBuckets int currentAppStandbyBucket =
      UsageStatsManager.STANDBY_BUCKET_ACTIVE;

  @UsageSource
  private static int currentUsageSource = UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;

  // This map will sort events by time, but otherwise will preserve order events were added in
  private static final NavigableMap> eventsByTimeStamp =
      Maps.synchronizedNavigableMap(Maps.newTreeMap());

  /**
   * Keys {@link UsageStats} objects by intervalType (e.g. {@link
   * UsageStatsManager#INTERVAL_WEEKLY}).
   */
  private static SetMultimap usageStatsByIntervalType =
      Multimaps.synchronizedSetMultimap(HashMultimap.create());

  private static final Map appStandbyBuckets = Maps.newConcurrentMap();

  /** Used with T APIs for {@link BroadcastResponseStats}. */
  private static final Map>
      appBroadcastStats = Maps.newConcurrentMap();

  /**
   * App usage observer registered via {@link UsageStatsManager#registerAppUsageObserver(int,
   * String[], long, TimeUnit, PendingIntent)}.
   */
  @AutoValue
  public abstract static class AppUsageObserver {

    public static AppUsageObserver build(
        int observerId,
        @NonNull Collection packageNames,
        long timeLimit,
        @NonNull TimeUnit timeUnit,
        @NonNull PendingIntent callbackIntent) {
      return new AutoValue_ShadowUsageStatsManager_AppUsageObserver(
          observerId, ImmutableList.copyOf(packageNames), timeLimit, timeUnit, callbackIntent);
    }

    public abstract int getObserverId();

    @NonNull
    public abstract ImmutableList getPackageNames();

    public abstract long getTimeLimit();

    @NonNull
    public abstract TimeUnit getTimeUnit();

    @NonNull
    public abstract PendingIntent getCallbackIntent();
  }

  private static final Map appUsageObserversById =
      Maps.newConcurrentMap();

  /**
   * Usage session observer registered via {@link
   * UsageStatsManager#registerUsageSessionObserver(int, String[], long, TimeUnit, long, TimeUnit,
   * PendingIntent, PendingIntent)}.
   */
  @AutoValue
  public abstract static class UsageSessionObserver {
    public static UsageSessionObserver build(
        int observerId,
        @NonNull List packageNames,
        Duration sessionStepDuration,
        Duration thresholdDuration,
        @NonNull PendingIntent sessionStepTriggeredIntent,
        @NonNull PendingIntent sessionEndedIntent) {
      return new AutoValue_ShadowUsageStatsManager_UsageSessionObserver(
          observerId,
          ImmutableList.copyOf(packageNames),
          sessionStepDuration,
          thresholdDuration,
          sessionStepTriggeredIntent,
          sessionEndedIntent);
    }

    public abstract int getObserverId();

    @NonNull
    public abstract ImmutableList getPackageNames();

    @Nullable
    public abstract Duration getSessionStepDuration();

    @Nullable
    public abstract Duration getThresholdDuration();

    @NonNull
    public abstract PendingIntent getSessionStepTriggeredIntent();

    @NonNull
    public abstract PendingIntent getSessionEndedIntent();
  }

  protected static final Map usageSessionObserversById =
      new LinkedHashMap<>();

  /**
   * App usage limit observer registered via {@link
   * UsageStatsManager#registerAppUsageLimitObserver(int, String[], Duration, Duration,
   * PendingIntent)}.
   */
  public static final class AppUsageLimitObserver {
    private final int observerId;
    private final ImmutableList packageNames;
    private final Duration timeLimit;
    private final Duration timeUsed;
    private final PendingIntent callbackIntent;

    public AppUsageLimitObserver(
        int observerId,
        @NonNull List packageNames,
        @NonNull Duration timeLimit,
        @NonNull Duration timeUsed,
        @NonNull PendingIntent callbackIntent) {
      this.observerId = observerId;
      this.packageNames = ImmutableList.copyOf(packageNames);
      this.timeLimit = checkNotNull(timeLimit);
      this.timeUsed = checkNotNull(timeUsed);
      this.callbackIntent = checkNotNull(callbackIntent);
    }

    public int getObserverId() {
      return observerId;
    }

    @NonNull
    public ImmutableList getPackageNames() {
      return packageNames;
    }

    @NonNull
    public Duration getTimeLimit() {
      return timeLimit;
    }

    @NonNull
    public Duration getTimeUsed() {
      return timeUsed;
    }

    @NonNull
    public PendingIntent getCallbackIntent() {
      return callbackIntent;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      AppUsageLimitObserver that = (AppUsageLimitObserver) o;
      return observerId == that.observerId
          && packageNames.equals(that.packageNames)
          && timeLimit.equals(that.timeLimit)
          && timeUsed.equals(that.timeUsed)
          && callbackIntent.equals(that.callbackIntent);
    }

    @Override
    public int hashCode() {
      int result = observerId;
      result = 31 * result + packageNames.hashCode();
      result = 31 * result + timeLimit.hashCode();
      result = 31 * result + timeUsed.hashCode();
      result = 31 * result + callbackIntent.hashCode();
      return result;
    }
  }

  private static final Map appUsageLimitObserversById =
      Maps.newConcurrentMap();

  @Implementation
  protected UsageEvents queryEvents(long beginTime, long endTime) {
    List results =
        ImmutableList.copyOf(
            Iterables.concat(eventsByTimeStamp.subMap(beginTime, endTime).values()));
    return createUsageEvents(results);
  }

  @Implementation(minSdk = V.SDK_INT)
  protected UsageEvents queryEvents(@ClassName("android.app.usage.UsageEventsQuery") Object query) {
    UsageEventsQueryReflector queryReflector = reflector(UsageEventsQueryReflector.class, query);
    long beginTime = queryReflector.getBeginTimeMillis();
    long endTime = queryReflector.getEndTimeMillis();
    int[] eventTypes = queryReflector.getEventTypes();
    ImmutableSet eventTypesSet = ImmutableSet.copyOf(Ints.asList(eventTypes));
    List results = new ArrayList<>();
    for (Event event : Iterables.concat(eventsByTimeStamp.subMap(beginTime, endTime).values())) {
      if (eventTypesSet.contains(event.getEventType())) {
        results.add(event);
      }
    }
    return createUsageEvents(results);
  }

  private static UsageEvents createUsageEvents(List results) {
    ArraySet names = new ArraySet<>();
    for (Event result : results) {
      if (result.mPackage != null) {
        names.add(result.mPackage);
      }
      if (result.mClass != null) {
        names.add(result.mClass);
      }
    }

    String[] table = names.toArray(new String[0]);
    Arrays.sort(table);

    // We can't directly construct usable UsageEvents, so we replicate what the framework does:
    // First the system marshalls the usage events into a Parcel.
    UsageEvents usageEvents = new UsageEvents(results, table);
    Parcel parcel = Parcel.obtain();
    usageEvents.writeToParcel(parcel, 0);
    // Then the app unmarshalls the usage events from the Parcel.
    parcel.setDataPosition(0);
    return new UsageEvents(parcel);
  }

  @Implementation(minSdk = Build.VERSION_CODES.P)
  protected UsageEvents queryEventsForSelf(long beginTime, long endTime) {
    String packageName = RuntimeEnvironment.getApplication().getOpPackageName();
    ImmutableList.Builder listBuilder = new ImmutableList.Builder<>();
    for (Event event : Iterables.concat(eventsByTimeStamp.subMap(beginTime, endTime).values())) {
      if (packageName.equals(event.getPackageName())) {
        listBuilder.add(event);
      }
    }
    return createUsageEvents(listBuilder.build());
  }

  /**
   * Adds an event to be returned by {@link UsageStatsManager#queryEvents}.
   *
   * 

This method won't affect the results of {@link #queryUsageStats} method. * * @deprecated Use {@link #addEvent(Event)} and {@link EventBuilder} instead. */ @Deprecated public void addEvent(String packageName, long timeStamp, int eventType) { EventBuilder eventBuilder = EventBuilder.buildEvent() .setPackage(packageName) .setTimeStamp(timeStamp) .setEventType(eventType); if (eventType == Event.CONFIGURATION_CHANGE) { eventBuilder.setConfiguration(new Configuration()); } addEvent(eventBuilder.build()); } /** * Adds an event to be returned by {@link UsageStatsManager#queryEvents}. * *

This method won't affect the results of {@link #queryUsageStats} method. * *

The {@link Event} can be built by {@link EventBuilder}. */ public void addEvent(Event event) { List eventsAtTime = eventsByTimeStamp.get(event.getTimeStamp()); if (eventsAtTime == null) { eventsAtTime = new ArrayList<>(1); eventsByTimeStamp.put(event.getTimeStamp(), eventsAtTime); } eventsAtTime.add(event); } /** * Simulates the operations done by the framework when there is a time change. If the time is * changed, the timestamps of all existing usage events will be shifted by the same offset as the * time change, in order to make sure they remain stable relative to the new time. * *

This method won't affect the results of {@link #queryUsageStats} method. * * @param offsetToAddInMillis the offset to be applied to all events. For example, if {@code * offsetInMillis} is 60,000, then all {@link Event}s will be shifted forward by 1 minute * (into the future). Likewise, if {@code offsetInMillis} is -60,000, then all {@link Event}s * will be shifted backward by 1 minute (into the past). */ public void simulateTimeChange(long offsetToAddInMillis) { List oldEvents = ImmutableList.copyOf(Iterables.concat(eventsByTimeStamp.values())); eventsByTimeStamp.clear(); for (Event event : oldEvents) { long newTimestamp = event.getTimeStamp() + offsetToAddInMillis; addEvent(EventBuilder.fromEvent(event).setTimeStamp(newTimestamp).build()); } } /** * Returns aggregated UsageStats added by calling {@link #addUsageStats}. * *

The real implementation creates these aggregated objects from individual {@link Event}. This * aggregation logic is nontrivial, so the shadow implementation just returns the aggregate data * added using {@link #addUsageStats}. */ @Implementation protected List queryUsageStats(int intervalType, long beginTime, long endTime) { List results = new ArrayList<>(); Range queryRange = Range.closed(beginTime, endTime); for (UsageStats stats : usageStatsByIntervalType.get(intervalType)) { Range statsRange = Range.closed(stats.getFirstTimeStamp(), stats.getLastTimeStamp()); if (queryRange.isConnected(statsRange)) { results.add(stats); } } return results; } /** * Adds an aggregated {@code UsageStats} object, to be returned by {@link #queryUsageStats}. * Construct these objects with {@link UsageStatsBuilder}, and set the firstTimestamp and * lastTimestamp fields to make time filtering work in {@link #queryUsageStats}. * * @param intervalType An interval type constant, e.g. {@link UsageStatsManager#INTERVAL_WEEKLY}. */ public void addUsageStats(int intervalType, UsageStats stats) { usageStatsByIntervalType.put(intervalType, stats); } /** * Returns the current standby bucket of the specified app that is set by {@code * setAppStandbyBucket}. If the standby bucket value has never been set, return {@link * UsageStatsManager.STANDBY_BUCKET_ACTIVE}. */ @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi public @StandbyBuckets int getAppStandbyBucket(String packageName) { Integer bucket = appStandbyBuckets.get(packageName); return (bucket == null) ? UsageStatsManager.STANDBY_BUCKET_ACTIVE : bucket; } @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi public Map getAppStandbyBuckets() { return new HashMap<>(appStandbyBuckets); } /** Sets the standby bucket of the specified app. */ @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi public void setAppStandbyBucket(String packageName, @StandbyBuckets int bucket) { appStandbyBuckets.put(packageName, bucket); } @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi public void setAppStandbyBuckets(Map appBuckets) { appStandbyBuckets.putAll(appBuckets); } @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi protected void registerAppUsageObserver( int observerId, String[] packages, long timeLimit, TimeUnit timeUnit, PendingIntent callbackIntent) { appUsageObserversById.put( observerId, AppUsageObserver.build( observerId, ImmutableList.copyOf(packages), timeLimit, timeUnit, callbackIntent)); } @Implementation(minSdk = Build.VERSION_CODES.P) @HiddenApi protected void unregisterAppUsageObserver(int observerId) { appUsageObserversById.remove(observerId); } /** Returns the {@link AppUsageObserver}s currently registered in {@link UsageStatsManager}. */ public Collection getRegisteredAppUsageObservers() { return ImmutableList.copyOf(appUsageObserversById.values()); } /** * Triggers a currently registered {@link AppUsageObserver} with {@code observerId}. * *

The observer will be no longer registered afterwards. */ public void triggerRegisteredAppUsageObserver(int observerId, long timeUsedInMillis) { AppUsageObserver observer = appUsageObserversById.remove(observerId); long timeLimitInMillis = observer.getTimeUnit().toMillis(observer.getTimeLimit()); Intent intent = new Intent() .putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId) .putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimitInMillis) .putExtra(UsageStatsManager.EXTRA_TIME_USED, timeUsedInMillis); try { observer.getCallbackIntent().send(RuntimeEnvironment.getApplication(), 0, intent); } catch (CanceledException e) { throw new RuntimeException(e); } } @Implementation(minSdk = Build.VERSION_CODES.Q) protected void registerUsageSessionObserver( int observerId, String[] packages, Duration sessionStepDuration, Duration thresholdTimeDuration, PendingIntent sessionStepTriggeredIntent, PendingIntent sessionEndedIntent) { usageSessionObserversById.put( observerId, UsageSessionObserver.build( observerId, ImmutableList.copyOf(packages), sessionStepDuration, thresholdTimeDuration, sessionStepTriggeredIntent, sessionEndedIntent)); } @Implementation(minSdk = Build.VERSION_CODES.Q) protected void unregisterUsageSessionObserver(int observerId) { usageSessionObserversById.remove(observerId); } /** * Returns the {@link UsageSessionObserver}s currently registered in {@link UsageStatsManager}. */ public List getRegisteredUsageSessionObservers() { return ImmutableList.copyOf(usageSessionObserversById.values()); } /** * Triggers a currently registered {@link UsageSessionObserver} with {@code observerId}. * *

The observer SHOULD be registered afterwards. */ public void triggerRegisteredSessionStepObserver(int observerId, long timeUsedInMillis) { UsageSessionObserver observer = usageSessionObserversById.get(observerId); long sessionStepTimeInMillis = observer.getSessionStepDuration().toMillis(); Intent intent = new Intent() .putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId) .putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, sessionStepTimeInMillis) .putExtra(UsageStatsManager.EXTRA_TIME_USED, timeUsedInMillis); try { observer.getSessionStepTriggeredIntent().send(RuntimeEnvironment.getApplication(), 0, intent); } catch (CanceledException e) { throw new RuntimeException(e); } } /** * Triggers a currently registered {@link UsageSessionObserver} with {@code observerId}. * *

The observer SHOULD be registered afterwards. */ public void triggerRegisteredSessionEndedObserver(int observerId) { UsageSessionObserver observer = usageSessionObserversById.get(observerId); Intent intent = new Intent().putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId); try { observer.getSessionEndedIntent().send(RuntimeEnvironment.getApplication(), 0, intent); } catch (CanceledException e) { throw new RuntimeException(e); } } /** * Registers an app usage limit observer that receives a callback on {@code callbackIntent} when * the sum of usages of apps and tokens in {@code observedEntities} exceeds {@code timeLimit - * timeUsed}. */ @Implementation(minSdk = Build.VERSION_CODES.Q) @HiddenApi protected void registerAppUsageLimitObserver( int observerId, String[] observedEntities, Duration timeLimit, Duration timeUsed, PendingIntent callbackIntent) { appUsageLimitObserversById.put( observerId, new AppUsageLimitObserver( observerId, ImmutableList.copyOf(observedEntities), timeLimit, timeUsed, callbackIntent)); } /** Unregisters the app usage limit observer specified by {@code observerId}. */ @Implementation(minSdk = Build.VERSION_CODES.Q) @HiddenApi protected void unregisterAppUsageLimitObserver(int observerId) { appUsageLimitObserversById.remove(observerId); } /** * Returns the {@link AppUsageLimitObserver}s currently registered in {@link UsageStatsManager}. */ public ImmutableList getRegisteredAppUsageLimitObservers() { return ImmutableList.copyOf(appUsageLimitObserversById.values()); } /** * Triggers a currently registered {@link AppUsageLimitObserver} with {@code observerId}. * *

The observer will still be registered afterwards. */ public void triggerRegisteredAppUsageLimitObserver(int observerId, Duration timeUsed) { AppUsageLimitObserver observer = appUsageLimitObserversById.get(observerId); Intent intent = new Intent() .putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId) .putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, observer.timeLimit.toMillis()) .putExtra(UsageStatsManager.EXTRA_TIME_USED, timeUsed.toMillis()); try { observer.callbackIntent.send(RuntimeEnvironment.getApplication(), 0, intent); } catch (CanceledException e) { throw new RuntimeException(e); } } /** * Returns the current app's standby bucket that is set by {@code setCurrentAppStandbyBucket}. If * the standby bucket value has never been set, return {@link * UsageStatsManager.STANDBY_BUCKET_ACTIVE}. */ @Implementation(minSdk = Build.VERSION_CODES.P) @StandbyBuckets protected int getAppStandbyBucket() { return currentAppStandbyBucket; } /** Sets the current app's standby bucket */ public void setCurrentAppStandbyBucket(@StandbyBuckets int bucket) { currentAppStandbyBucket = bucket; } @Implementation(minSdk = Build.VERSION_CODES.Q) @UsageSource @HiddenApi protected int getUsageSource() { return currentUsageSource; } /** Sets what app usage observers will consider the source of usage for an activity. */ @TargetApi(Build.VERSION_CODES.Q) public void setUsageSource(@UsageSource int usageSource) { currentUsageSource = usageSource; } @SuppressWarnings("unchecked") @Implementation(minSdk = TIRAMISU) protected List queryBroadcastResponseStats( @Nullable String packageName, long id) { List result = new ArrayList<>(); for (Map.Entry> entry : appBroadcastStats.entrySet()) { if (packageName == null || entry.getKey().equals(packageName)) { result.addAll( (List) queryBroadcastResponseStatsForId(entry.getValue(), id)); } } return result; } private Object /* List */ queryBroadcastResponseStatsForId( Map idToResponseStats, long id) { List result = new ArrayList<>(); for (Map.Entry entry : idToResponseStats.entrySet()) { if (id == 0 || entry.getKey() == id) { result.add((BroadcastResponseStats) entry.getValue()); } } return result; } @Implementation(minSdk = TIRAMISU) protected void clearBroadcastResponseStats(@Nullable String packageName, long id) { for (Map.Entry> entry : appBroadcastStats.entrySet()) { if (packageName == null || entry.getKey().equals(packageName)) { clearBroadcastResponseStatsForId(entry.getValue(), id); } } appBroadcastStats.values().removeIf(Map::isEmpty); } private void clearBroadcastResponseStatsForId( Map idToResponseStats, long idToRemove) { idToResponseStats.keySet().removeIf(id -> id == idToRemove || idToRemove == 0); } @TargetApi(Build.VERSION_CODES.TIRAMISU) public void addBroadcastResponseStats(Object /*BroadcastResponseStats*/ statsObject) { BroadcastResponseStats stats = (BroadcastResponseStats) statsObject; Map idToStats = appBroadcastStats.computeIfAbsent( stats.getPackageName(), unused -> Maps.newConcurrentMap()); idToStats.put(stats.getId(), stats); } @Resetter public static void reset() { currentAppStandbyBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; currentUsageSource = UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY; eventsByTimeStamp.clear(); appStandbyBuckets.clear(); appUsageObserversById.clear(); usageSessionObserversById.clear(); appUsageLimitObserversById.clear(); usageStatsByIntervalType.clear(); appBroadcastStats.clear(); } /** * Builder for constructing {@link UsageStats} objects. The constructor of UsageStats is not part * of the Android API. */ public static class UsageStatsBuilder { private UsageStats usageStats = new UsageStats(); // Use {@link #newBuilder} to construct builders. private UsageStatsBuilder() {} public static UsageStatsBuilder newBuilder() { return new UsageStatsBuilder(); } public UsageStats build() { return usageStats; } public UsageStatsBuilder setPackageName(String packageName) { usageStats.mPackageName = packageName; return this; } public UsageStatsBuilder setFirstTimeStamp(long firstTimeStamp) { usageStats.mBeginTimeStamp = firstTimeStamp; return this; } public UsageStatsBuilder setLastTimeStamp(long lastTimeStamp) { usageStats.mEndTimeStamp = lastTimeStamp; return this; } public UsageStatsBuilder setTotalTimeInForeground(long totalTimeInForeground) { usageStats.mTotalTimeInForeground = totalTimeInForeground; return this; } public UsageStatsBuilder setLastTimeUsed(long lastTimeUsed) { usageStats.mLastTimeUsed = lastTimeUsed; return this; } } /** * Builder for constructing {@link Event} objects. The fields of Event are not part of the Android * API. */ public static class EventBuilder { private Event event = new Event(); private EventBuilder() {} public static EventBuilder fromEvent(Event event) { EventBuilder eventBuilder = new EventBuilder() .setPackage(event.mPackage) .setClass(event.mClass) .setTimeStamp(event.mTimeStamp) .setEventType(event.mEventType) .setConfiguration(event.mConfiguration); if (event.mEventType == Event.CONFIGURATION_CHANGE) { eventBuilder.setConfiguration(new Configuration()); } return eventBuilder; } public static EventBuilder buildEvent() { return new EventBuilder(); } public Event build() { return event; } public EventBuilder setPackage(String packageName) { event.mPackage = packageName; return this; } public EventBuilder setClass(String className) { event.mClass = className; return this; } public EventBuilder setTimeStamp(long timeStamp) { event.mTimeStamp = timeStamp; return this; } public EventBuilder setEventType(int eventType) { event.mEventType = eventType; return this; } public EventBuilder setConfiguration(Configuration configuration) { event.mConfiguration = configuration; return this; } public EventBuilder setShortcutId(String shortcutId) { event.mShortcutId = shortcutId; return this; } @TargetApi(Build.VERSION_CODES.Q) public EventBuilder setInstanceId(int instanceId) { event.mInstanceId = instanceId; return this; } @TargetApi(Build.VERSION_CODES.Q) public EventBuilder setTaskRootPackage(String taskRootPackage) { event.mTaskRootPackage = taskRootPackage; return this; } @TargetApi(Build.VERSION_CODES.Q) public EventBuilder setTaskRootClass(String taskRootClass) { event.mTaskRootClass = taskRootClass; return this; } @TargetApi(Build.VERSION_CODES.P) public EventBuilder setAppStandbyBucket(int bucket) { event.mBucketAndReason &= 0xFFFF; event.mBucketAndReason |= bucket << 16; return this; } @TargetApi(V.SDK_INT) public EventBuilder setExtras(PersistableBundle extras) { EventReflector eventReflector = reflector(EventReflector.class, event); eventReflector.setExtras(extras); return this; } } // TODO: remove reflection calls once Android V is fully supported. @ForType(className = "android.app.usage.UsageEventsQuery") interface UsageEventsQueryReflector { int[] getEventTypes(); long getBeginTimeMillis(); long getEndTimeMillis(); } @ForType(Event.class) interface EventReflector { @Accessor("mExtras") void setExtras(PersistableBundle extras); PersistableBundle getExtras(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy