package com.android.systemui;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.IDockedStackListener;
import android.view.WindowManagerGlobal;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.function.Consumer;
/**
* Utility wrapper to listen for whether or not a docked stack exists, to be
* used for things like the different overview icon in that mode.
*/
-public class DockedStackExistsListener extends IDockedStackListener.Stub {
+public class DockedStackExistsListener {
private static final String TAG = "DockedStackExistsListener";
- private final Consumer<Boolean> mCallback;
+ private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
- private DockedStackExistsListener(Consumer<Boolean> callback) {
- mCallback = callback;
- }
+ static {
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
+ new IDockedStackListener.Stub() {
+ @Override
+ public void onDividerVisibilityChanged(boolean b) throws RemoteException {
- @Override
- public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
- }
+ }
- @Override
- public void onDockedStackExistsChanged(final boolean exists) throws RemoteException {
- mCallback.accept(exists);
- }
+ @Override
+ public void onDockedStackExistsChanged(boolean exists)
+ throws RemoteException {
+ DockedStackExistsListener.onDockedStackExistsChanged(exists);
+ }
- @Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
- boolean isHomeStackResizable) throws RemoteException {
- }
+ @Override
+ public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onAdjustedForImeChanged(boolean b, long l)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onDockSideChanged(int i) throws RemoteException {
- @Override
- public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
- throws RemoteException {
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed registering docked stack exists listener", e);
+ }
}
- @Override
- public void onDockSideChanged(int newDockSide) throws RemoteException {
+
+ private static void onDockedStackExistsChanged(boolean exists) {
+ synchronized (sCallbacks) {
+ sCallbacks.removeIf(wf -> wf.get() == null);
+ sCallbacks.forEach(wf -> wf.get().accept(exists));
+ }
}
public static void register(Consumer<Boolean> callback) {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- new DockedStackExistsListener(callback));
- } catch (RemoteException e) {
- Log.e(TAG, "Failed registering docked stack exists listener", e);
+ synchronized (sCallbacks) {
+ sCallbacks.add(new WeakReference<>(callback));
}
}
}
mFragmentHostManager.getFragmentManager().beginTransaction()
.replace(id, (Fragment) mExtension.get(), mTag)
.commit();
+ mExtension.clearItem(false);
}
@Override
} catch (ClassCastException e) {
Log.e(TAG, extension.getClass().getName() + " must be a Fragment", e);
}
+ mExtension.clearItem(true);
}
public static <T> void attachExtensonToFragment(View view, String tag, int id,
* (like configuration) may have changed.
*/
T reload();
+
+ /**
+ * Null out the cached item for the purpose of memory saving, should only be done
+ * when any other references are already gotten.
+ * @param isDestroyed
+ */
+ void clearItem(boolean isDestroyed);
}
interface ExtensionBuilder<T> {
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
import java.util.ArrayList;
import java.util.Collections;
return get();
}
+ @Override
+ public void clearItem(boolean isDestroyed) {
+ if (isDestroyed && mItem != null) {
+ Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ }
+ mItem = null;
+ }
+
private void notifyChanged() {
+ if (mItem != null) {
+ Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ }
mItem = null;
for (int i = 0; i < mProducers.size(); i++) {
final T item = mProducers.get(i).get();
}
public void addTunerFactory(TunerFactory<T> factory, String[] keys) {
- mProducers.add(new TunerItem(factory, factory.keys()));
+ mProducers.add(new TunerItem(factory, keys));
}
public void addUiMode(int uiMode, Supplier<T> mode) {
import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import com.android.systemui.Dependency;
// TODO(b/35345376): Turn this back on for debuggable builds after known leak fixed.
private static final boolean ENABLED = Build.IS_DEBUGGABLE
&& SystemProperties.getBoolean("debug.enable_leak_reporting", false);
+ private static final String FORCE_ENABLE = "sysui_force_garbage_monitor";
private GarbageMonitor mGarbageMonitor;
@Override
public void start() {
- if (!ENABLED) {
+ boolean forceEnable = Settings.Secure.getInt(mContext.getContentResolver(),
+ FORCE_ENABLE, 0) != 0;
+ if (!ENABLED && !forceEnable) {
return;
}
mGarbageMonitor = Dependency.get(GarbageMonitor.class);
@Override
public void destroy() {
+ mAccessibility.destroy();
mController.removeCallback(mControllerCallbackH);
+ if (mZenFooter != null) {
+ mZenFooter.cleanup();
+ }
+ Dependency.get(TunerService.class).removeTunable(this);
+ mHandler.removeCallbacksAndMessages(null);
}
private void initDialog() {
}
});
mDialogView.setAccessibilityDelegate(this);
- mAccessibilityMgr.addAccessibilityStateChangeListener(
- new AccessibilityStateChangeListener() {
- @Override
- public void onAccessibilityStateChanged(boolean enabled) {
- updateFeedbackEnabled();
- }
- });
+ mAccessibilityMgr.addAccessibilityStateChangeListener(mListener);
updateFeedbackEnabled();
}
+ public void destroy() {
+ mAccessibilityMgr.removeAccessibilityStateChangeListener(mListener);
+ }
+
@Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
AccessibilityEvent event) {
}
return false;
}
+
+ private final AccessibilityStateChangeListener mListener =
+ enabled -> updateFeedbackEnabled();
}
private static class VolumeRow {
}
@Override
+ public void clearItem(boolean isDestroyed) {
+
+ }
+
+ @Override
public Context getContext() {
return null;
}