ExtensionControllerImpl.java revision 3b9357f3b9d6f2dae175ad1b92116e17b6df1493
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.statusbar.policy;
16
17import android.content.Context;
18import android.content.res.Configuration;
19import android.os.Handler;
20import android.util.ArrayMap;
21
22import com.android.systemui.Dependency;
23import com.android.systemui.plugins.Plugin;
24import com.android.systemui.plugins.PluginListener;
25import com.android.systemui.plugins.PluginManager;
26import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
27import com.android.systemui.tuner.TunerService;
28import com.android.systemui.tuner.TunerService.Tunable;
29import com.android.systemui.util.leak.LeakDetector;
30
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.Comparator;
34import java.util.function.Consumer;
35import java.util.function.Supplier;
36
37public class ExtensionControllerImpl implements ExtensionController {
38
39    public static final int SORT_ORDER_PLUGIN  = 0;
40    public static final int SORT_ORDER_TUNER   = 1;
41    public static final int SORT_ORDER_UI_MODE = 2;
42    public static final int SORT_ORDER_DEFAULT = 3;
43
44    private final Context mDefaultContext;
45
46    public ExtensionControllerImpl(Context context) {
47        mDefaultContext = context;
48    }
49
50    @Override
51    public <T> ExtensionBuilder<T> newExtension(Class<T> cls) {
52        return new ExtensionBuilder<>();
53    }
54
55    private interface Producer<T> {
56        T get();
57
58        void destroy();
59    }
60
61    private class ExtensionBuilder<T> implements ExtensionController.ExtensionBuilder<T> {
62
63        private ExtensionImpl<T> mExtension = new ExtensionImpl<>();
64
65        @Override
66        public ExtensionController.ExtensionBuilder<T> withTunerFactory(TunerFactory<T> factory) {
67            mExtension.addTunerFactory(factory, factory.keys());
68            return this;
69        }
70
71        @Override
72        public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
73            return withPlugin(cls, PluginManager.getAction(cls));
74        }
75
76        @Override
77        public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
78                String action) {
79            return withPlugin(cls, action, null);
80        }
81
82        @Override
83        public <P> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
84                String action, PluginConverter<T, P> converter) {
85            mExtension.addPlugin(action, cls, converter);
86            return this;
87        }
88
89        @Override
90        public ExtensionController.ExtensionBuilder<T> withDefault(Supplier<T> def) {
91            mExtension.addDefault(def);
92            return this;
93        }
94
95        public ExtensionController.ExtensionBuilder<T> withUiMode(int uiMode,
96                Supplier<T> supplier) {
97            mExtension.addUiMode(uiMode, supplier);
98            return this;
99        }
100
101        @Override
102        public ExtensionController.ExtensionBuilder<T> withCallback(
103                Consumer<T> callback) {
104            mExtension.mCallbacks.add(callback);
105            return this;
106        }
107
108        @Override
109        public ExtensionController.Extension build() {
110            // Manually sort, plugins first, tuners second, defaults last.
111            Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder));
112            mExtension.notifyChanged();
113            return mExtension;
114        }
115    }
116
117    private class ExtensionImpl<T> implements ExtensionController.Extension<T> {
118        private final ArrayList<Item<T>> mProducers = new ArrayList<>();
119        private final ArrayList<Consumer<T>> mCallbacks = new ArrayList<>();
120        private T mItem;
121        private Context mPluginContext;
122
123        public void addCallback(Consumer<T> callback) {
124            mCallbacks.add(callback);
125        }
126
127        @Override
128        public T get() {
129            return mItem;
130        }
131
132        @Override
133        public Context getContext() {
134            return mPluginContext != null ? mPluginContext : mDefaultContext;
135        }
136
137        @Override
138        public void destroy() {
139            for (int i = 0; i < mProducers.size(); i++) {
140                mProducers.get(i).destroy();
141            }
142        }
143
144        @Override
145        public T reload() {
146            notifyChanged();
147            return get();
148        }
149
150        @Override
151        public void clearItem(boolean isDestroyed) {
152            if (isDestroyed && mItem != null) {
153                Dependency.get(LeakDetector.class).trackGarbage(mItem);
154            }
155            mItem = null;
156        }
157
158        private void notifyChanged() {
159            if (mItem != null) {
160                Dependency.get(LeakDetector.class).trackGarbage(mItem);
161            }
162            mItem = null;
163            for (int i = 0; i < mProducers.size(); i++) {
164                final T item = mProducers.get(i).get();
165                if (item != null) {
166                    mItem = item;
167                    break;
168                }
169            }
170            for (int i = 0; i < mCallbacks.size(); i++) {
171                mCallbacks.get(i).accept(mItem);
172            }
173        }
174
175        public void addDefault(Supplier<T> def) {
176            mProducers.add(new Default(def));
177        }
178
179        public <P> void addPlugin(String action, Class<P> cls, PluginConverter<T, P> converter) {
180            mProducers.add(new PluginItem(action, cls, converter));
181        }
182
183        public void addTunerFactory(TunerFactory<T> factory, String[] keys) {
184            mProducers.add(new TunerItem(factory, keys));
185        }
186
187        public void addUiMode(int uiMode, Supplier<T> mode) {
188            mProducers.add(new UiModeItem(uiMode, mode));
189        }
190
191        private class PluginItem<P extends Plugin> implements Item<T>, PluginListener<P> {
192            private final PluginConverter<T, P> mConverter;
193            private T mItem;
194
195            public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
196                mConverter = converter;
197                Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
198            }
199
200            @Override
201            public void onPluginConnected(P plugin, Context pluginContext) {
202                mPluginContext = pluginContext;
203                if (mConverter != null) {
204                    mItem = mConverter.getInterfaceFromPlugin(plugin);
205                } else {
206                    mItem = (T) plugin;
207                }
208                notifyChanged();
209            }
210
211            @Override
212            public void onPluginDisconnected(P plugin) {
213                mPluginContext = null;
214                mItem = null;
215                notifyChanged();
216            }
217
218            @Override
219            public T get() {
220                return mItem;
221            }
222
223            @Override
224            public void destroy() {
225                Dependency.get(PluginManager.class).removePluginListener(this);
226            }
227
228            @Override
229            public int sortOrder() {
230                return SORT_ORDER_PLUGIN;
231            }
232        }
233
234        private class TunerItem<T> implements Item<T>, Tunable {
235            private final TunerFactory<T> mFactory;
236            private final ArrayMap<String, String> mSettings = new ArrayMap<>();
237            private T mItem;
238
239            public TunerItem(TunerFactory<T> factory, String... setting) {
240                mFactory = factory;
241                Dependency.get(TunerService.class).addTunable(this, setting);
242            }
243
244            @Override
245            public T get() {
246                return mItem;
247            }
248
249            @Override
250            public void destroy() {
251                Dependency.get(TunerService.class).removeTunable(this);
252            }
253
254            @Override
255            public void onTuningChanged(String key, String newValue) {
256                mSettings.put(key, newValue);
257                mItem = mFactory.create(mSettings);
258                notifyChanged();
259            }
260
261            @Override
262            public int sortOrder() {
263                return SORT_ORDER_TUNER;
264            }
265        }
266
267        private class UiModeItem<T> implements Item<T>, ConfigurationListener {
268
269            private final int mDesiredUiMode;
270            private final Supplier<T> mSupplier;
271            private int mUiMode;
272            private Handler mHandler = new Handler();
273
274            public UiModeItem(int uiMode, Supplier<T> supplier) {
275                mDesiredUiMode = uiMode;
276                mSupplier = supplier;
277                mUiMode = mDefaultContext.getResources().getConfiguration().uiMode;
278                Dependency.get(ConfigurationController.class).addCallback(this);
279            }
280
281            @Override
282            public void onConfigChanged(Configuration newConfig) {
283                int newMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
284                if (newMode != mUiMode) {
285                    mUiMode = newMode;
286                    // Post to make sure we don't have concurrent modifications.
287                    mHandler.post(ExtensionImpl.this::notifyChanged);
288                }
289            }
290
291            @Override
292            public T get() {
293                return (mUiMode == mDesiredUiMode) ? mSupplier.get() : null;
294            }
295
296            @Override
297            public void destroy() {
298                Dependency.get(ConfigurationController.class).removeCallback(this);
299            }
300
301            @Override
302            public int sortOrder() {
303                return SORT_ORDER_UI_MODE;
304            }
305        }
306
307        private class Default<T> implements Item<T> {
308            private final Supplier<T> mSupplier;
309
310            public Default(Supplier<T> supplier) {
311                mSupplier = supplier;
312            }
313
314            @Override
315            public T get() {
316                return mSupplier.get();
317            }
318
319            @Override
320            public void destroy() {
321
322            }
323
324            @Override
325            public int sortOrder() {
326                return SORT_ORDER_DEFAULT;
327            }
328        }
329    }
330
331    private interface Item<T> extends Producer<T> {
332        int sortOrder();
333    }
334}
335