Merge "Incorporating event bus to proxy recents events."
This commit is contained in:
@@ -13,3 +13,11 @@
|
||||
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
|
||||
-keep class com.android.systemui.statusbar.tv.TvStatusBar
|
||||
-keep class com.android.systemui.recents.*
|
||||
|
||||
-keepclassmembers class ** {
|
||||
public void onBusEvent(**);
|
||||
public void onInterprocessBusEvent(**);
|
||||
}
|
||||
-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
|
||||
public <init>(android.os.Bundle);
|
||||
}
|
||||
@@ -74,6 +74,8 @@ import java.util.ArrayList;
|
||||
public class Recents extends SystemUI
|
||||
implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
|
||||
|
||||
public final static int EVENT_BUS_PRIORITY = 1;
|
||||
|
||||
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
|
||||
final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
|
||||
final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.recents.misc.Console;
|
||||
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.recents.model.RecentsPackageMonitor;
|
||||
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
|
||||
import com.android.systemui.recents.model.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
@@ -53,7 +54,10 @@ import java.util.ArrayList;
|
||||
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
|
||||
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
|
||||
|
||||
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
RecentsPackageMonitor mPackageMonitor;
|
||||
long mLastTabKeyEventTime;
|
||||
|
||||
// Top level views
|
||||
@@ -324,6 +328,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
|
||||
mConfig = RecentsConfiguration.initialize(this, ssp);
|
||||
mConfig.update(this, ssp, ssp.getWindowRect());
|
||||
mPackageMonitor = new RecentsPackageMonitor();
|
||||
|
||||
// Initialize the widget host (the host id is static and does not change)
|
||||
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
|
||||
@@ -371,7 +376,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
registerReceiver(mServiceBroadcastReceiver, filter);
|
||||
|
||||
// Register any broadcast receivers for the task loader
|
||||
loader.registerReceivers(this, mRecentsView);
|
||||
mPackageMonitor.register(this);
|
||||
|
||||
// Update the recent tasks
|
||||
updateRecentsTasks();
|
||||
@@ -415,7 +420,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
unregisterReceiver(mServiceBroadcastReceiver);
|
||||
|
||||
// Unregister any broadcast receivers for the task loader
|
||||
loader.unregisterReceivers();
|
||||
mPackageMonitor.unregister();
|
||||
|
||||
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
|
||||
// through SystemUI, we need to reset the config launch flags to ensure that we do not
|
||||
|
||||
@@ -0,0 +1,844 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.events;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.MutableBoolean;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a subscriber, which implements various event bus handler methods.
|
||||
*/
|
||||
class Subscriber {
|
||||
private WeakReference<Object> mSubscriber;
|
||||
|
||||
long registrationTime;
|
||||
|
||||
Subscriber(Object subscriber, long registrationTime) {
|
||||
mSubscriber = new WeakReference<>(subscriber);
|
||||
this.registrationTime = registrationTime;
|
||||
}
|
||||
|
||||
public String toString(int priority) {
|
||||
Object sub = mSubscriber.get();
|
||||
String id = Integer.toHexString(System.identityHashCode(sub));
|
||||
return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
|
||||
}
|
||||
|
||||
public Object getReference() {
|
||||
return mSubscriber.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an event handler with a priority.
|
||||
*/
|
||||
class EventHandler {
|
||||
int priority;
|
||||
Subscriber subscriber;
|
||||
EventHandlerMethod method;
|
||||
|
||||
EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
|
||||
this.subscriber = subscriber;
|
||||
this.method = method;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return subscriber.toString(priority) + " " + method.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the low level method handling a particular event.
|
||||
*/
|
||||
class EventHandlerMethod {
|
||||
private Method mMethod;
|
||||
Class<? extends EventBus.Event> eventType;
|
||||
|
||||
EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
|
||||
mMethod = method;
|
||||
mMethod.setAccessible(true);
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
public void invoke(Object target, EventBus.Event event)
|
||||
throws InvocationTargetException, IllegalAccessException {
|
||||
mMethod.invoke(target, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple in-process event bus. It is simple because we can make assumptions about the state of
|
||||
* SystemUI and Recent's lifecycle.
|
||||
*
|
||||
* <p>
|
||||
* Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
|
||||
* on the main application thread. Publishers can send() events to synchronously call subscribers
|
||||
* of that event, or post() events to be processed in the next run of the {@link Looper}. In
|
||||
* addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
|
||||
* (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
|
||||
* Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
|
||||
* correctly across processes.
|
||||
*
|
||||
* <p>
|
||||
* Subscribers must be registered with a particular EventBus before they will receive events, and
|
||||
* handler methods must match a specific signature.
|
||||
*
|
||||
* <p>
|
||||
* Event method signature:<ul>
|
||||
* <li>Methods must be public final
|
||||
* <li>Methods must return void
|
||||
* <li>Methods must be called "onBusEvent"
|
||||
* <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Interprocess-Event method signature:<ul>
|
||||
* <li>Methods must be public final
|
||||
* <li>Methods must return void
|
||||
* <li>Methods must be called "onInterprocessBusEvent"
|
||||
* <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* </p>
|
||||
* Each subscriber can be registered with a given priority (default 1), and events will be dispatch
|
||||
* in decreasing order of priority. For subscribers with the same priority, events will be
|
||||
* dispatched by latest registration time to earliest.
|
||||
*
|
||||
* <p>
|
||||
* Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
|
||||
* takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent
|
||||
* across processes.
|
||||
*
|
||||
* <p>
|
||||
* Caveats:<ul>
|
||||
* <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
|
||||
* there must be another strong reference to the publisher for it to not get garbage-collected and
|
||||
* continue receiving events.
|
||||
* <li>Because the event handlers are called back using reflection, the EventBus is not intended
|
||||
* for use in tight, performance criticial loops. For most user input/system callback events, this
|
||||
* is generally of low enough frequency to use the EventBus.
|
||||
* <li>Because the event handlers are called back using reflection, there will often be no
|
||||
* references to them from actual code. The proguard configuration will be need to be updated to
|
||||
* keep these extra methods:
|
||||
*
|
||||
* -keepclassmembers class ** {
|
||||
* public void onBusEvent(**);
|
||||
* public void onInterprocessBusEvent(**);
|
||||
* }
|
||||
* -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
|
||||
* public <init>(android.os.Bundle);
|
||||
* }
|
||||
*
|
||||
* <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}. This
|
||||
* is only done once per class type, but if possible, it is best to pre-register an instance of
|
||||
* that class beforehand or when idle.
|
||||
* <li>Each event should be sent once. Events may hold internal information about the current
|
||||
* dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
|
||||
* so it may be unsafe to edit, change, or re-send the event again.
|
||||
* <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
|
||||
* initialized by the constructor and read by each subscriber of that event. Subscribers should
|
||||
* never alter events as they are processed, and this enforces that pattern.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Future optimizations:
|
||||
* <li>throw exception/log when a subscriber loses the reference
|
||||
* <li>trace cost per registration & invocation
|
||||
* <li>trace cross-process invocation
|
||||
* <li>register(subscriber, Class<?>...) -- pass in exact class types you want registered
|
||||
* <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
|
||||
* <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
|
||||
* message before invocation (ie. check if task id == this task id)
|
||||
* <li>add postOnce() which automatically debounces
|
||||
* <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
|
||||
* <li>consolidate register() and registerInterprocess()
|
||||
* <li>sendForResult<ReturnType>(Event) to send and get a result, but who will send the
|
||||
* result?
|
||||
* </p>
|
||||
*/
|
||||
public class EventBus extends BroadcastReceiver {
|
||||
|
||||
public static final String TAG = "EventBus";
|
||||
|
||||
/**
|
||||
* An event super class that allows us to track internal event state across subscriber
|
||||
* invocations.
|
||||
*
|
||||
* Events should not be edited by subscribers.
|
||||
*/
|
||||
public static class Event {
|
||||
// Indicates that this event's dispatch should be traced and logged to logcat
|
||||
boolean trace;
|
||||
// Indicates that this event must be posted on the EventBus's looper thread before invocation
|
||||
boolean requiresPost;
|
||||
// Not currently exposed, allows a subscriber to cancel further dispatch of this event
|
||||
boolean cancelled;
|
||||
|
||||
// Only accessible from derived events
|
||||
protected Event() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An inter-process event super class that allows us to track user state across subscriber
|
||||
* invocations.
|
||||
*/
|
||||
public static class InterprocessEvent extends Event {
|
||||
private static final String EXTRA_USER = "_user";
|
||||
|
||||
// The user which this event originated from
|
||||
public final int user;
|
||||
|
||||
// Only accessible from derived events
|
||||
protected InterprocessEvent(int user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the event bus
|
||||
*/
|
||||
protected InterprocessEvent(Bundle b) {
|
||||
user = b.getInt(EXTRA_USER);
|
||||
}
|
||||
|
||||
protected Bundle toBundle() {
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(EXTRA_USER, user);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proguard must also know, and keep, all methods matching this signature.
|
||||
*
|
||||
* -keepclassmembers class ** {
|
||||
* public void onBusEvent(**);
|
||||
* public void onInterprocessBusEvent(**);
|
||||
* }
|
||||
*/
|
||||
private static final String METHOD_PREFIX = "onBusEvent";
|
||||
private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
|
||||
|
||||
// Ensures that interprocess events can only be sent from a process holding this permission. */
|
||||
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
|
||||
|
||||
// Used for passing event data across process boundaries
|
||||
private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
|
||||
|
||||
// The default priority of all subscribers
|
||||
private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
|
||||
|
||||
// Used for debugging everything
|
||||
private static final boolean DEBUG_TRACE_ALL = false;
|
||||
|
||||
// Orders the handlers by priority and registration time
|
||||
private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
|
||||
@Override
|
||||
public int compare(EventHandler h1, EventHandler h2) {
|
||||
// Rank the handlers by priority descending, followed by registration time descending.
|
||||
// aka. the later registered
|
||||
if (h1.priority != h2.priority) {
|
||||
return h2.priority - h1.priority;
|
||||
} else {
|
||||
return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used for initializing the default bus
|
||||
private static final Object sLock = new Object();
|
||||
private static EventBus sDefaultBus;
|
||||
|
||||
// The handler to post all events
|
||||
private Handler mHandler;
|
||||
|
||||
// Keep track of whether we have registered a broadcast receiver already, so that we can
|
||||
// unregister ourselves before re-registering again with a new IntentFilter.
|
||||
private boolean mHasRegisteredReceiver;
|
||||
|
||||
/**
|
||||
* Map from event class -> event handler list. Keeps track of the actual mapping from event
|
||||
* to subscriber method.
|
||||
*/
|
||||
private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map from subscriber class -> event handler method lists. Used to determine upon registration
|
||||
* of a new subscriber whether we need to read all the subscriber's methods again using
|
||||
* reflection or whether we can just add the subscriber to the event type map.
|
||||
*/
|
||||
private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map from interprocess event name -> interprocess event class. Used for mapping the event
|
||||
* name after receiving the broadcast, to the event type. After which a new instance is created
|
||||
* and posted in the local process.
|
||||
*/
|
||||
private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Set of all currently registered subscribers
|
||||
*/
|
||||
private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
|
||||
|
||||
// For tracing
|
||||
private int mCallCount;
|
||||
private long mCallDurationMicros;
|
||||
|
||||
/**
|
||||
* Private constructor to create an event bus for a given looper.
|
||||
*/
|
||||
private EventBus(Looper looper) {
|
||||
mHandler = new Handler(looper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the default event bus for the application's main thread.
|
||||
*/
|
||||
public static EventBus getDefault() {
|
||||
if (sDefaultBus == null)
|
||||
synchronized (sLock) {
|
||||
if (sDefaultBus == null) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("New EventBus");
|
||||
}
|
||||
sDefaultBus = new EventBus(Looper.getMainLooper());
|
||||
}
|
||||
}
|
||||
return sDefaultBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a subscriber to receive events with the default priority.
|
||||
*
|
||||
* @param subscriber the subscriber to handle events. If this is the first instance of the
|
||||
* subscriber's class type that has been registered, the class's methods will
|
||||
* be scanned for appropriate event handler methods.
|
||||
*/
|
||||
public void register(Object subscriber) {
|
||||
registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a subscriber to receive events with the given priority.
|
||||
*
|
||||
* @param subscriber the subscriber to handle events. If this is the first instance of the
|
||||
* subscriber's class type that has been registered, the class's methods will
|
||||
* be scanned for appropriate event handler methods.
|
||||
* @param priority the priority that this subscriber will receive events relative to other
|
||||
* subscribers
|
||||
*/
|
||||
public void register(Object subscriber, int priority) {
|
||||
registerSubscriber(subscriber, priority, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly registers a subscriber to receive interprocess events with the default priority.
|
||||
*
|
||||
* @param subscriber the subscriber to handle events. If this is the first instance of the
|
||||
* subscriber's class type that has been registered, the class's methods will
|
||||
* be scanned for appropriate event handler methods.
|
||||
*/
|
||||
public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
|
||||
registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a subscriber to receive interprocess events with the given priority.
|
||||
*
|
||||
* @param subscriber the subscriber to handle events. If this is the first instance of the
|
||||
* subscriber's class type that has been registered, the class's methods will
|
||||
* be scanned for appropriate event handler methods.
|
||||
* @param priority the priority that this subscriber will receive events relative to other
|
||||
* subscribers
|
||||
*/
|
||||
public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// Register the subscriber normally, and update the broadcast receiver filter if this is
|
||||
// a new subscriber type with interprocess events
|
||||
MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
|
||||
registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
|
||||
}
|
||||
if (hasInterprocessEventsChanged.value) {
|
||||
registerReceiverForInterprocessEvents(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all EventHandlers pointing to the specified subscriber. This does not remove the
|
||||
* mapping of subscriber type to event handler method, in case new instances of this subscriber
|
||||
* are registered.
|
||||
*/
|
||||
public void unregister(Object subscriber) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("unregister()");
|
||||
}
|
||||
|
||||
// Fail immediately if we are being called from the non-main thread
|
||||
long callingThreadId = Thread.currentThread().getId();
|
||||
if (callingThreadId != mHandler.getLooper().getThread().getId()) {
|
||||
throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
|
||||
}
|
||||
|
||||
// Return early if this is not a registered subscriber
|
||||
if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> subscriberType = subscriber.getClass();
|
||||
ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
|
||||
if (subscriberMethods != null) {
|
||||
// For each of the event handlers the subscriber handles, remove all references of that
|
||||
// handler
|
||||
for (EventHandlerMethod method : subscriberMethods) {
|
||||
ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
|
||||
for (int i = eventHandlers.size() - 1; i >= 0; i--) {
|
||||
if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
|
||||
eventHandlers.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit unregistration for interprocess event subscribers. This actually behaves exactly
|
||||
* the same as unregister() since we also do not want to stop listening for specific
|
||||
* inter-process messages in case new instances of that subscriber is registered.
|
||||
*/
|
||||
public void unregisterInterprocess(Context context, Object subscriber) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("unregisterInterprocess()");
|
||||
}
|
||||
unregister(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an event to the subscribers of the given event type immediately. This can only be
|
||||
* called from the same thread as the EventBus's looper thread (for the default EventBus, this
|
||||
* is the main application thread).
|
||||
*/
|
||||
public void send(Event event) {
|
||||
// Fail immediately if we are being called from the non-main thread
|
||||
long callingThreadId = Thread.currentThread().getId();
|
||||
if (callingThreadId != mHandler.getLooper().getThread().getId()) {
|
||||
throw new RuntimeException("Can not send() a message from a non-main thread.");
|
||||
}
|
||||
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("send(" + event.getClass().getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// Reset the event's cancelled state
|
||||
event.requiresPost = false;
|
||||
event.cancelled = false;
|
||||
queueEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a message to the subscribers of the given event type. The messages will be posted on
|
||||
* the EventBus's looper thread (for the default EventBus, this is the main application thread).
|
||||
*/
|
||||
public void post(Event event) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("post(" + event.getClass().getSimpleName() + ")");
|
||||
}
|
||||
|
||||
// Reset the event's cancelled state
|
||||
event.requiresPost = true;
|
||||
event.cancelled = false;
|
||||
queueEvent(event);
|
||||
}
|
||||
|
||||
/** Prevent post()ing an InterprocessEvent */
|
||||
@Deprecated
|
||||
public void post(InterprocessEvent event) {
|
||||
throw new RuntimeException("Not supported, use postInterprocess");
|
||||
}
|
||||
|
||||
/** Prevent send()ing an InterprocessEvent */
|
||||
@Deprecated
|
||||
public void send(InterprocessEvent event) {
|
||||
throw new RuntimeException("Not supported, use postInterprocess");
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts an interprocess event.
|
||||
*/
|
||||
public void postInterprocess(Context context, final InterprocessEvent event) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
|
||||
}
|
||||
String eventType = event.getClass().getName();
|
||||
Bundle eventBundle = event.toBundle();
|
||||
Intent intent = new Intent(eventType);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
|
||||
Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
context.sendBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver for interprocess events.
|
||||
*/
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
|
||||
}
|
||||
|
||||
Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
|
||||
Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
|
||||
try {
|
||||
Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
|
||||
send((Event) ctor.newInstance(eventBundle));
|
||||
} catch (NoSuchMethodException|
|
||||
InvocationTargetException|
|
||||
InstantiationException|
|
||||
IllegalAccessException e) {
|
||||
Log.e(TAG, "Failed to create InterprocessEvent", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a dump of the current state of the EventBus
|
||||
*/
|
||||
public String dump() {
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("Registered class types:");
|
||||
output.append("\n");
|
||||
for (Class<?> clz : mSubscriberTypeMap.keySet()) {
|
||||
output.append("\t");
|
||||
output.append(clz.getSimpleName());
|
||||
output.append("\n");
|
||||
}
|
||||
output.append("Event map:");
|
||||
output.append("\n");
|
||||
for (Class<?> clz : mEventTypeMap.keySet()) {
|
||||
output.append("\t");
|
||||
output.append(clz.getSimpleName());
|
||||
output.append(" -> ");
|
||||
output.append("\n");
|
||||
ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
|
||||
for (EventHandler handler : handlers) {
|
||||
Object subscriber = handler.subscriber.getReference();
|
||||
if (subscriber != null) {
|
||||
String id = Integer.toHexString(System.identityHashCode(subscriber));
|
||||
output.append("\t\t");
|
||||
output.append(subscriber.getClass().getSimpleName());
|
||||
output.append(" [0x" + id + ", #" + handler.priority + "]");
|
||||
output.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new subscriber.
|
||||
*
|
||||
* @return return whether or not this
|
||||
*/
|
||||
private void registerSubscriber(Object subscriber, int priority,
|
||||
MutableBoolean hasInterprocessEventsChangedOut) {
|
||||
// Fail immediately if we are being called from the non-main thread
|
||||
long callingThreadId = Thread.currentThread().getId();
|
||||
if (callingThreadId != mHandler.getLooper().getThread().getId()) {
|
||||
throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
|
||||
}
|
||||
|
||||
// Return immediately if this exact subscriber is already registered
|
||||
if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
|
||||
return;
|
||||
}
|
||||
|
||||
long t1 = 0;
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
t1 = SystemClock.currentTimeMicro();
|
||||
logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
|
||||
}
|
||||
Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
|
||||
Class<?> subscriberType = subscriber.getClass();
|
||||
ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
|
||||
if (subscriberMethods != null) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("Subscriber class type already registered");
|
||||
}
|
||||
|
||||
// If we've parsed this subscriber type before, just add to the set for all the known
|
||||
// events
|
||||
for (EventHandlerMethod method : subscriberMethods) {
|
||||
ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
|
||||
eventTypeHandlers.add(new EventHandler(sub, method, priority));
|
||||
sortEventHandlersByPriority(eventTypeHandlers);
|
||||
}
|
||||
mSubscribers.add(sub);
|
||||
return;
|
||||
} else {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("Subscriber class type requires registration");
|
||||
}
|
||||
|
||||
// If we are parsing this type from scratch, ensure we add it to the subscriber type
|
||||
// map, and pull out he handler methods below
|
||||
subscriberMethods = new ArrayList<>();
|
||||
mSubscriberTypeMap.put(subscriberType, subscriberMethods);
|
||||
mSubscribers.add(sub);
|
||||
}
|
||||
|
||||
// Find all the valid event bus handler methods of the subscriber
|
||||
MutableBoolean isInterprocessEvent = new MutableBoolean(false);
|
||||
Method[] methods = subscriberType.getMethods();
|
||||
for (Method m : methods) {
|
||||
Class<?>[] parameterTypes = m.getParameterTypes();
|
||||
isInterprocessEvent.value = false;
|
||||
if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
|
||||
Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
|
||||
ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
|
||||
if (eventTypeHandlers == null) {
|
||||
eventTypeHandlers = new ArrayList<>();
|
||||
mEventTypeMap.put(eventType, eventTypeHandlers);
|
||||
}
|
||||
if (isInterprocessEvent.value) {
|
||||
try {
|
||||
// Enforce that the event must have a Bundle constructor
|
||||
eventType.getConstructor(Bundle.class);
|
||||
|
||||
mInterprocessEventNameMap.put(eventType.getName(),
|
||||
(Class<? extends InterprocessEvent>) eventType);
|
||||
if (hasInterprocessEventsChangedOut != null) {
|
||||
hasInterprocessEventsChangedOut.value = true;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
|
||||
}
|
||||
}
|
||||
EventHandlerMethod method = new EventHandlerMethod(m, eventType);
|
||||
EventHandler handler = new EventHandler(sub, method, priority);
|
||||
eventTypeHandlers.add(handler);
|
||||
subscriberMethods.add(method);
|
||||
sortEventHandlersByPriority(eventTypeHandlers);
|
||||
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid(" * Method: " + m.getName() +
|
||||
" event: " + parameterTypes[0].getSimpleName() +
|
||||
" interprocess? " + isInterprocessEvent.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
|
||||
(SystemClock.currentTimeMicro() - t1) + " microseconds");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new message.
|
||||
*/
|
||||
private void queueEvent(final Event event) {
|
||||
ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
|
||||
if (eventHandlers == null) {
|
||||
return;
|
||||
}
|
||||
// We need to clone the list in case a subscriber unregisters itself during traversal
|
||||
eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
|
||||
for (final EventHandler eventHandler : eventHandlers) {
|
||||
if (eventHandler.subscriber.getReference() != null) {
|
||||
if (event.requiresPost) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processEvent(eventHandler, event);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
processEvent(eventHandler, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes and dispatches the given event to the given event handler, on the thread of whoever
|
||||
* calls this method.
|
||||
*/
|
||||
private void processEvent(final EventHandler eventHandler, final Event event) {
|
||||
// Skip if the event was already cancelled
|
||||
if (event.cancelled) {
|
||||
if (event.trace || DEBUG_TRACE_ALL) {
|
||||
logWithPid("Event dispatch cancelled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (event.trace || DEBUG_TRACE_ALL) {
|
||||
logWithPid(" -> " + eventHandler.toString());
|
||||
}
|
||||
Object sub = eventHandler.subscriber.getReference();
|
||||
if (sub != null) {
|
||||
long t1 = 0;
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
t1 = SystemClock.currentTimeMicro();
|
||||
}
|
||||
eventHandler.method.invoke(sub, event);
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
long duration = (SystemClock.currentTimeMicro() - t1);
|
||||
mCallDurationMicros += duration;
|
||||
mCallCount++;
|
||||
logWithPid(eventHandler.method.toString() + " duration: " + duration +
|
||||
" microseconds, avg: " + (mCallDurationMicros / mCallCount));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Failed to deliver event to null subscriber");
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(TAG, "Failed to invoke method", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-registers the broadcast receiver for any new messages that we want to listen for.
|
||||
*/
|
||||
private void registerReceiverForInterprocessEvents(Context context) {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid("registerReceiverForInterprocessEvents()");
|
||||
}
|
||||
// Rebuild the receiver filter with the new interprocess events
|
||||
IntentFilter filter = new IntentFilter();
|
||||
for (String eventName : mInterprocessEventNameMap.keySet()) {
|
||||
filter.addAction(eventName);
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
logWithPid(" filter: " + eventName);
|
||||
}
|
||||
}
|
||||
// Re-register the receiver with the new filter
|
||||
if (mHasRegisteredReceiver) {
|
||||
context.unregisterReceiver(this);
|
||||
}
|
||||
context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
|
||||
mHasRegisteredReceiver = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
|
||||
* is true, then remove the subscriber before returning.
|
||||
*/
|
||||
private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
|
||||
for (int i = mSubscribers.size() - 1; i >= 0; i--) {
|
||||
Subscriber sub = mSubscribers.get(i);
|
||||
if (sub.getReference() == subscriber) {
|
||||
if (removeFoundSubscriber) {
|
||||
mSubscribers.remove(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether {@param method} is a valid (normal or interprocess) event bus handler method
|
||||
*/
|
||||
private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
|
||||
MutableBoolean isInterprocessEventOut) {
|
||||
int modifiers = method.getModifiers();
|
||||
if (Modifier.isPublic(modifiers) &&
|
||||
Modifier.isFinal(modifiers) &&
|
||||
method.getReturnType().equals(Void.TYPE) &&
|
||||
parameterTypes.length == 1) {
|
||||
if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
|
||||
method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
|
||||
isInterprocessEventOut.value = true;
|
||||
return true;
|
||||
} else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
|
||||
method.getName().startsWith(METHOD_PREFIX)) {
|
||||
isInterprocessEventOut.value = false;
|
||||
return true;
|
||||
} else {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
|
||||
logWithPid(" Expected method take an Event-based parameter: " + method.getName());
|
||||
} else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
|
||||
!method.getName().startsWith(METHOD_PREFIX)) {
|
||||
logWithPid(" Expected method start with method prefix: " + method.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DEBUG_TRACE_ALL) {
|
||||
if (!Modifier.isPublic(modifiers)) {
|
||||
logWithPid(" Expected method to be public: " + method.getName());
|
||||
} else if (!Modifier.isFinal(modifiers)) {
|
||||
logWithPid(" Expected method to be final: " + method.getName());
|
||||
} else if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
logWithPid(" Expected method to return null: " + method.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the event handlers by priority and registration time.
|
||||
*/
|
||||
private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
|
||||
Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to log the given {@param text} with the current process and user id.
|
||||
*/
|
||||
private static void logWithPid(String text) {
|
||||
Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.events.activity;
|
||||
|
||||
import com.android.systemui.recents.events.EventBus;
|
||||
import com.android.systemui.recents.model.RecentsPackageMonitor;
|
||||
import com.android.systemui.recents.views.TaskStackView;
|
||||
|
||||
/**
|
||||
* This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes.
|
||||
* {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
|
||||
* packages.
|
||||
*/
|
||||
public class PackagesChangedEvent extends EventBus.Event {
|
||||
|
||||
public final RecentsPackageMonitor monitor;
|
||||
public final String packageName;
|
||||
public final int userId;
|
||||
|
||||
public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) {
|
||||
this.monitor = monitor;
|
||||
this.packageName = packageName;
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import android.content.Context;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.systemui.recents.events.EventBus;
|
||||
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -31,18 +33,9 @@ import java.util.List;
|
||||
* Recents list.
|
||||
*/
|
||||
public class RecentsPackageMonitor extends PackageMonitor {
|
||||
public interface PackageCallbacks {
|
||||
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName,
|
||||
int userId);
|
||||
}
|
||||
|
||||
PackageCallbacks mCb;
|
||||
SystemServicesProxy mSystemServicesProxy;
|
||||
|
||||
/** Registers the broadcast receivers with the specified callbacks. */
|
||||
public void register(Context context, PackageCallbacks cb) {
|
||||
mSystemServicesProxy = new SystemServicesProxy(context);
|
||||
mCb = cb;
|
||||
public void register(Context context) {
|
||||
try {
|
||||
// We register for events from all users, but will cross-reference them with
|
||||
// packages for the current user and any profiles they have
|
||||
@@ -60,17 +53,13 @@ public class RecentsPackageMonitor extends PackageMonitor {
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mSystemServicesProxy = null;
|
||||
mCb = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, int uid) {
|
||||
if (mCb == null) return;
|
||||
|
||||
// Notify callbacks that a package has changed
|
||||
final int eventUserId = getChangingUserId();
|
||||
mCb.onPackagesChanged(this, packageName, eventUserId);
|
||||
EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,11 +70,9 @@ public class RecentsPackageMonitor extends PackageMonitor {
|
||||
|
||||
@Override
|
||||
public void onPackageModified(String packageName) {
|
||||
if (mCb == null) return;
|
||||
|
||||
// Notify callbacks that a package has changed
|
||||
final int eventUserId = getChangingUserId();
|
||||
mCb.onPackagesChanged(this, packageName, eventUserId);
|
||||
EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +95,8 @@ public class RecentsPackageMonitor extends PackageMonitor {
|
||||
// If we know that the component still exists in the package, then skip
|
||||
continue;
|
||||
}
|
||||
if (mSystemServicesProxy.getActivityInfo(cn, userId) != null) {
|
||||
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
|
||||
if (ssp.getActivityInfo(cn, userId) != null) {
|
||||
existingComponents.add(cn);
|
||||
} else {
|
||||
removedComponents.add(cn);
|
||||
|
||||
@@ -262,8 +262,6 @@ public class RecentsTaskLoader {
|
||||
TaskResourceLoadQueue mLoadQueue;
|
||||
TaskResourceLoader mLoader;
|
||||
|
||||
RecentsPackageMonitor mPackageMonitor;
|
||||
|
||||
int mMaxThumbnailCacheSize;
|
||||
int mMaxIconCacheSize;
|
||||
int mNumVisibleTasksLoaded;
|
||||
@@ -293,7 +291,6 @@ public class RecentsTaskLoader {
|
||||
|
||||
// Initialize the proxy, cache and loaders
|
||||
mSystemServicesProxy = new SystemServicesProxy(context);
|
||||
mPackageMonitor = new RecentsPackageMonitor();
|
||||
mLoadQueue = new TaskResourceLoadQueue();
|
||||
mApplicationIconCache = new DrawableLruCache(iconCacheSize);
|
||||
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
|
||||
@@ -519,17 +516,6 @@ public class RecentsTaskLoader {
|
||||
mLoadQueue.clearTasks();
|
||||
}
|
||||
|
||||
/** Registers any broadcast receivers. */
|
||||
public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
|
||||
// Register the broadcast receiver to handle messages related to packages being added/removed
|
||||
mPackageMonitor.register(context, cb);
|
||||
}
|
||||
|
||||
/** Unregisters any broadcast receivers. */
|
||||
public void unregisterReceivers() {
|
||||
mPackageMonitor.unregister();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signals from the system, trimming memory when requested to prevent us from running
|
||||
* out of memory.
|
||||
|
||||
@@ -59,8 +59,7 @@ import java.util.List;
|
||||
* This view is the the top level layout that contains TaskStacks (which are laid out according
|
||||
* to their SpaceNode bounds.
|
||||
*/
|
||||
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
|
||||
RecentsPackageMonitor.PackageCallbacks {
|
||||
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
|
||||
|
||||
private static final String TAG = "RecentsView";
|
||||
|
||||
@@ -732,14 +731,4 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
mCb.onTaskResize(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
|
||||
// Propagate this event down to each task stack view
|
||||
if (mTaskStackView != null) {
|
||||
mTaskStackView.onPackagesChanged(monitor, packageName, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,13 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsActivityLaunchState;
|
||||
import com.android.systemui.recents.RecentsActivity;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.events.EventBus;
|
||||
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
|
||||
import com.android.systemui.recents.misc.DozeTrigger;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.RecentsPackageMonitor;
|
||||
import com.android.systemui.recents.model.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
@@ -54,7 +56,7 @@ import java.util.List;
|
||||
/* The visual representation of a task stack view */
|
||||
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
|
||||
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
|
||||
ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
|
||||
ViewPool.ViewPoolConsumer<TaskView, Task> {
|
||||
|
||||
/** The TaskView callbacks */
|
||||
interface TaskStackViewCallbacks {
|
||||
@@ -77,7 +79,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
TaskStackViewTouchHandler mTouchHandler;
|
||||
TaskStackViewCallbacks mCb;
|
||||
ViewPool<TaskView, Task> mViewPool;
|
||||
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
|
||||
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
|
||||
DozeTrigger mUIDozeTrigger;
|
||||
DismissView mDismissAllButton;
|
||||
boolean mDismissAllButtonAnimating;
|
||||
@@ -97,9 +99,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
Matrix mTmpMatrix = new Matrix();
|
||||
Rect mTmpRect = new Rect();
|
||||
TaskViewTransform mTmpTransform = new TaskViewTransform();
|
||||
HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
|
||||
ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
|
||||
List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
|
||||
HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
|
||||
ArrayList<TaskView> mTaskViews = new ArrayList<>();
|
||||
List<TaskView> mImmutableTaskViews = new ArrayList<>();
|
||||
LayoutInflater mInflater;
|
||||
boolean mLayersDisabled;
|
||||
|
||||
@@ -147,6 +149,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
/** Sets the task stack */
|
||||
void setStack(TaskStack stack) {
|
||||
// Set the new stack
|
||||
@@ -1430,13 +1444,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
postInvalidateOnAnimation();
|
||||
}
|
||||
|
||||
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
|
||||
/**** EventBus Events ****/
|
||||
|
||||
@Override
|
||||
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
|
||||
public final void onBusEvent(PackagesChangedEvent event) {
|
||||
// Compute which components need to be removed
|
||||
HashSet<ComponentName> removedComponents = monitor.computeComponentsRemoved(
|
||||
mStack.getTaskKeys(), packageName, userId);
|
||||
HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved(
|
||||
mStack.getTaskKeys(), event.packageName, event.userId);
|
||||
|
||||
// For other tasks, just remove them directly if they no longer exist
|
||||
ArrayList<Task> tasks = mStack.getTasks();
|
||||
|
||||
Reference in New Issue
Block a user