/* * Copyright (C) 2017 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.statusbar.policy; import android.content.Context; import android.content.res.Configuration; import android.os.Handler; import android.util.ArrayMap; import com.android.systemui.Dependency; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; 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; import java.util.Comparator; import java.util.function.Consumer; import java.util.function.Supplier; public class ExtensionControllerImpl implements ExtensionController { public static final int SORT_ORDER_PLUGIN = 0; public static final int SORT_ORDER_TUNER = 1; public static final int SORT_ORDER_FEATURE = 2; public static final int SORT_ORDER_UI_MODE = 3; public static final int SORT_ORDER_DEFAULT = 4; private final Context mDefaultContext; public ExtensionControllerImpl(Context context) { mDefaultContext = context; } @Override public ExtensionBuilder newExtension(Class cls) { return new ExtensionBuilder<>(); } private interface Producer { T get(); void destroy(); } private class ExtensionBuilder implements ExtensionController.ExtensionBuilder { private ExtensionImpl mExtension = new ExtensionImpl<>(); @Override public ExtensionController.ExtensionBuilder withTunerFactory(TunerFactory factory) { mExtension.addTunerFactory(factory, factory.keys()); return this; } @Override public

ExtensionController.ExtensionBuilder withPlugin(Class

cls) { return withPlugin(cls, PluginManager.getAction(cls)); } @Override public

ExtensionController.ExtensionBuilder withPlugin(Class

cls, String action) { return withPlugin(cls, action, null); } @Override public

ExtensionController.ExtensionBuilder withPlugin(Class

cls, String action, PluginConverter converter) { mExtension.addPlugin(action, cls, converter); return this; } @Override public ExtensionController.ExtensionBuilder withDefault(Supplier def) { mExtension.addDefault(def); return this; } @Override public ExtensionController.ExtensionBuilder withUiMode(int uiMode, Supplier supplier) { mExtension.addUiMode(uiMode, supplier); return this; } @Override public ExtensionController.ExtensionBuilder withFeature(String feature, Supplier supplier) { mExtension.addFeature(feature, supplier); return this; } @Override public ExtensionController.ExtensionBuilder withCallback( Consumer callback) { mExtension.mCallbacks.add(callback); return this; } @Override public ExtensionController.Extension build() { // Sort items in ascending order Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder)); mExtension.notifyChanged(); return mExtension; } } private class ExtensionImpl implements ExtensionController.Extension { private final ArrayList> mProducers = new ArrayList<>(); private final ArrayList> mCallbacks = new ArrayList<>(); private T mItem; private Context mPluginContext; public void addCallback(Consumer callback) { mCallbacks.add(callback); } @Override public T get() { return mItem; } @Override public Context getContext() { return mPluginContext != null ? mPluginContext : mDefaultContext; } @Override public void destroy() { for (int i = 0; i < mProducers.size(); i++) { mProducers.get(i).destroy(); } } @Override public T reload() { notifyChanged(); 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(); if (item != null) { mItem = item; break; } } for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).accept(mItem); } } public void addDefault(Supplier def) { mProducers.add(new Default(def)); } public

void addPlugin(String action, Class

cls, PluginConverter converter) { mProducers.add(new PluginItem(action, cls, converter)); } public void addTunerFactory(TunerFactory factory, String[] keys) { mProducers.add(new TunerItem(factory, keys)); } public void addUiMode(int uiMode, Supplier mode) { mProducers.add(new UiModeItem(uiMode, mode)); } public void addFeature(String feature, Supplier mode) { mProducers.add(new FeatureItem<>(feature, mode)); } private class PluginItem

implements Item, PluginListener

{ private final PluginConverter mConverter; private T mItem; public PluginItem(String action, Class

cls, PluginConverter converter) { mConverter = converter; Dependency.get(PluginManager.class).addPluginListener(action, this, cls); } @Override public void onPluginConnected(P plugin, Context pluginContext) { mPluginContext = pluginContext; if (mConverter != null) { mItem = mConverter.getInterfaceFromPlugin(plugin); } else { mItem = (T) plugin; } notifyChanged(); } @Override public void onPluginDisconnected(P plugin) { mPluginContext = null; mItem = null; notifyChanged(); } @Override public T get() { return mItem; } @Override public void destroy() { Dependency.get(PluginManager.class).removePluginListener(this); } @Override public int sortOrder() { return SORT_ORDER_PLUGIN; } } private class TunerItem implements Item, Tunable { private final TunerFactory mFactory; private final ArrayMap mSettings = new ArrayMap<>(); private T mItem; public TunerItem(TunerFactory factory, String... setting) { mFactory = factory; Dependency.get(TunerService.class).addTunable(this, setting); } @Override public T get() { return mItem; } @Override public void destroy() { Dependency.get(TunerService.class).removeTunable(this); } @Override public void onTuningChanged(String key, String newValue) { mSettings.put(key, newValue); mItem = mFactory.create(mSettings); notifyChanged(); } @Override public int sortOrder() { return SORT_ORDER_TUNER; } } private class UiModeItem implements Item, ConfigurationListener { private final int mDesiredUiMode; private final Supplier mSupplier; private int mUiMode; private Handler mHandler = new Handler(); public UiModeItem(int uiMode, Supplier supplier) { mDesiredUiMode = uiMode; mSupplier = supplier; mUiMode = mDefaultContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK; Dependency.get(ConfigurationController.class).addCallback(this); } @Override public void onConfigChanged(Configuration newConfig) { int newMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK; if (newMode != mUiMode) { mUiMode = newMode; // Post to make sure we don't have concurrent modifications. mHandler.post(ExtensionImpl.this::notifyChanged); } } @Override public T get() { return (mUiMode == mDesiredUiMode) ? mSupplier.get() : null; } @Override public void destroy() { Dependency.get(ConfigurationController.class).removeCallback(this); } @Override public int sortOrder() { return SORT_ORDER_UI_MODE; } } private class FeatureItem implements Item { private final String mFeature; private final Supplier mSupplier; public FeatureItem(String feature, Supplier supplier) { mSupplier = supplier; mFeature = feature; } @Override public T get() { return mDefaultContext.getPackageManager().hasSystemFeature(mFeature) ? mSupplier.get() : null; } @Override public void destroy() { } @Override public int sortOrder() { return SORT_ORDER_FEATURE; } } private class Default implements Item { private final Supplier mSupplier; public Default(Supplier supplier) { mSupplier = supplier; } @Override public T get() { return mSupplier.get(); } @Override public void destroy() { } @Override public int sortOrder() { return SORT_ORDER_DEFAULT; } } } private interface Item extends Producer { int sortOrder(); } }