Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9933c1cf authored by Jason Monk's avatar Jason Monk
Browse files

Extend extension support

 - Add support for ui mode type selection
 - Convert plugin fragment listener over to extensions to be more
   versatile
 - Add tests

Test: runtest systemui
Change-Id: Id5f28aa14b16e5a12df0434c9792bce551011efb
parent ecb3d113
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ public class Dependency extends SystemUI {
                new FragmentService(mContext));

        mProviders.put(ExtensionController.class, () ->
                new ExtensionControllerImpl());
                new ExtensionControllerImpl(mContext));

        mProviders.put(PluginDependencyProvider.class, () ->
                new PluginDependencyProvider(get(PluginManager.class)));
+64 −0
Original line number Diff line number Diff line
@@ -15,60 +15,50 @@
package com.android.systemui.fragments;

import android.app.Fragment;
import android.content.Context;
import android.util.Log;
import android.view.View;

import com.android.systemui.Dependency;
import com.android.systemui.plugins.FragmentBase;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;

public class PluginFragmentListener implements PluginListener<Plugin> {
import java.util.function.Consumer;

    private static final String TAG = "PluginFragmentListener";
/**
 * Wires up an Extension to a Fragment tag/id so that it always contains the class
 * selected by the extension.
 */
public class ExtensionFragmentListener<T extends FragmentBase> implements Consumer<T> {

    private static final String TAG = "ExtensionFragmentListener";

    private final FragmentHostManager mFragmentHostManager;
    private final PluginManager mPluginManager;
    private final Class<? extends Fragment> mDefaultClass;
    private final Class<? extends FragmentBase> mExpectedInterface;
    private final String mTag;
    private final Extension<T> mExtension;
    private String mOldClass;

    public PluginFragmentListener(View view, String tag, Class<? extends Fragment> defaultFragment,
            Class<? extends FragmentBase> expectedInterface) {
    private ExtensionFragmentListener(View view, String tag, int id, Extension<T> extension) {
        mTag = tag;
        mFragmentHostManager = FragmentHostManager.get(view);
        mPluginManager = Dependency.get(PluginManager.class);
        mExpectedInterface = expectedInterface;
        mDefaultClass = defaultFragment;
    }

    public void startListening() {
        mPluginManager.addPluginListener(this, mExpectedInterface,
                false /* Only allow one */);
    }

    public void stopListening() {
        mPluginManager.removePluginListener(this);
        mExtension = extension;
        mFragmentHostManager.getFragmentManager().beginTransaction()
                .replace(id, (Fragment) mExtension.get(), mTag)
                .commit();
    }

    @Override
    public void onPluginConnected(Plugin plugin, Context pluginContext) {
    public void accept(T extension) {
        try {
            mExpectedInterface.cast(plugin);
            Fragment.class.cast(plugin);
            mFragmentHostManager.getPluginManager().setCurrentPlugin(mTag,
                    plugin.getClass().getName(), pluginContext);
            Fragment.class.cast(extension);
            mFragmentHostManager.getExtensionManager().setCurrentExtension(mTag,
                    mOldClass, extension.getClass().getName(), mExtension.getContext());
            mOldClass = extension.getClass().getName();
        } catch (ClassCastException e) {
            Log.e(TAG, plugin.getClass().getName() + " must be a Fragment and implement "
                    + mExpectedInterface.getName(), e);
            Log.e(TAG, extension.getClass().getName() + " must be a Fragment", e);
        }
    }

    @Override
    public void onPluginDisconnected(Plugin plugin) {
        mFragmentHostManager.getPluginManager().removePlugin(mTag,
                plugin.getClass().getName(), mDefaultClass.getName());
    public static <T> void attachExtensonToFragment(View view, String tag, int id,
            Extension<T> extension) {
        extension.addCallback(new ExtensionFragmentListener(view, tag, id, extension));
    }
}
+15 −20
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,7 +52,7 @@ public class FragmentHostManager {
    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
            ActivityInfo.CONFIG_FONT_SCALE);
    private final FragmentService mManager;
    private final PluginFragmentManager mPlugins = new PluginFragmentManager();
    private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();

    private FragmentController mFragments;
    private FragmentLifecycleCallbacks mLifecycleCallbacks;
@@ -174,7 +175,7 @@ public class FragmentHostManager {
        return mFragments.getFragmentManager();
    }

    PluginFragmentManager getPluginManager() {
    ExtensionFragmentManager getExtensionManager() {
        return mPlugins;
    }

@@ -261,22 +262,16 @@ public class FragmentHostManager {
        }
    }

    class PluginFragmentManager {
        private final ArrayMap<String, Context> mPluginLookup = new ArrayMap<>();
    class ExtensionFragmentManager {
        private final ArrayMap<String, Context> mExtensionLookup = new ArrayMap<>();

        public void removePlugin(String tag, String currentClass, String defaultClass) {
        public void setCurrentExtension(@NonNull  String tag, @Nullable String oldClass,
                @NonNull String currentClass, @Nullable Context context) {
            Fragment fragment = getFragmentManager().findFragmentByTag(tag);
            mPluginLookup.remove(currentClass);
            getFragmentManager().beginTransaction()
                    .replace(((View) fragment.getView().getParent()).getId(),
                            instantiate(mContext, defaultClass, null), tag)
                    .commit();
            reloadFragments();
            if (oldClass != null) {
                mExtensionLookup.remove(oldClass);
            }

        public void setCurrentPlugin(String tag, String currentClass, Context context) {
            Fragment fragment = getFragmentManager().findFragmentByTag(tag);
            mPluginLookup.put(currentClass, context);
            mExtensionLookup.put(currentClass, context);
            getFragmentManager().beginTransaction()
                    .replace(((View) fragment.getView().getParent()).getId(),
                            instantiate(context, currentClass, null), tag)
@@ -292,11 +287,11 @@ public class FragmentHostManager {
        }

        Fragment instantiate(Context context, String className, Bundle arguments) {
            Context pluginContext = mPluginLookup.get(className);
            if (pluginContext != null) {
                Fragment f = Fragment.instantiate(pluginContext, className, arguments);
            Context extensionContext = mExtensionLookup.get(className);
            if (extensionContext != null) {
                Fragment f = Fragment.instantiate(extensionContext, className, arguments);
                if (f instanceof Plugin) {
                    ((Plugin) f).onCreate(mContext, pluginContext);
                    ((Plugin) f).onCreate(mContext, extensionContext);
                }
                return f;
            }
+51 −50
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;

import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -36,11 +37,17 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.TaskStackBuilder;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -49,8 +56,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -75,7 +85,9 @@ import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
@@ -88,63 +100,86 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.DateTimeView;
import android.widget.ImageView;
import android.widget.RemoteViews;
import android.widget.TextView;
import android.widget.Toast;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.DejankUtils;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.PluginFragmentListener;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
@@ -183,6 +218,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
@@ -190,11 +226,15 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
        .OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.volume.VolumeComponent;

@@ -205,50 +245,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.ActivityManager.StackId;
import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.NotificationChannel;
import android.app.RemoteInput;
import android.app.TaskStackBuilder;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.Handler;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.IWindowManager;
import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
import android.widget.Toast;

import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.DejankUtils;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.util.NotificationChannels;

import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

@@ -1131,11 +1131,12 @@ public class StatusBar extends SystemUI implements DemoMode,
        View container = mStatusBarWindow.findViewById(R.id.qs_frame);
        if (container != null) {
            FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
            fragmentHostManager.getFragmentManager().beginTransaction()
                    .replace(R.id.qs_frame, new QSFragment(), QS.TAG)
                    .commit();
            new PluginFragmentListener(container, QS.TAG, QSFragment.class, QS.class)
                    .startListening();
            ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
                    Dependency.get(ExtensionController.class).newExtension(QS.class)
                            .withPlugin(QS.class)
                            .withUiMode(UI_MODE_TYPE_CAR, () -> new QSFragment())
                            .withDefault(() -> new QSFragment())
                            .build());
            final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                    mIconController);
            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
+4 −2
Original line number Diff line number Diff line
@@ -14,8 +14,7 @@

package com.android.systemui.statusbar.policy;

import com.android.systemui.Dependency;
import com.android.systemui.plugins.Plugin;
import android.content.Context;

import java.util.Map;
import java.util.function.Consumer;
@@ -31,7 +30,9 @@ public interface ExtensionController {

    interface Extension<T> {
        T get();
        Context getContext();
        void destroy();
        void addCallback(Consumer<T> callback);
    }

    interface ExtensionBuilder<T> {
@@ -42,6 +43,7 @@ public interface ExtensionController {
                PluginConverter<T, P> converter);
        ExtensionBuilder<T> withDefault(Supplier<T> def);
        ExtensionBuilder<T> withCallback(Consumer<T> callback);
        ExtensionBuilder<T> withUiMode(int mode, Supplier<T> def);
        Extension build();
    }

Loading