ExtensionControllerImpl.java revision 769139da0397c5b2abf0f6d5fbcf3e2bd33276db
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;
29
30import java.util.ArrayList;
31import java.util.Collections;
32import java.util.Comparator;
33import java.util.function.Consumer;
34import java.util.function.Supplier;
35
36public class ExtensionControllerImpl implements ExtensionController {
37
38    public static final int SORT_ORDER_PLUGIN  = 0;
39    public static final int SORT_ORDER_TUNER   = 1;
40    public static final int SORT_ORDER_UI_MODE = 2;
41    public static final int SORT_ORDER_DEFAULT = 3;
42
43    private final Context mDefaultContext;
44
45    public ExtensionControllerImpl(Context context) {
46        mDefaultContext = context;
47    }
48
49    @Override
50    public <T> ExtensionBuilder<T> newExtension(Class<T> cls) {
51        return new ExtensionBuilder<>();
52    }
53
54    private interface Producer<T> {
55        T get();
56
57        void destroy();
58    }
59
60    private class ExtensionBuilder<T> implements ExtensionController.ExtensionBuilder<T> {
61
62        private ExtensionImpl<T> mExtension = new ExtensionImpl<>();
63
64        @Override
65        public ExtensionController.ExtensionBuilder<T> withTunerFactory(TunerFactory<T> factory) {
66            mExtension.addTunerFactory(factory, factory.keys());
67            return this;
68        }
69
70        @Override
71        public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
72            return withPlugin(cls, PluginManager.getAction(cls));
73        }
74
75        @Override
76        public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
77                String action) {
78            return withPlugin(cls, action, null);
79        }
80
81        @Override
82        public <P> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
83                String action, PluginConverter<T, P> converter) {
84            mExtension.addPlugin(action, cls, converter);
85            return this;
86        }
87
88        @Override
89        public ExtensionController.ExtensionBuilder<T> withDefault(Supplier<T> def) {
90            mExtension.addDefault(def);
91            return this;
92        }
93
94        public ExtensionController.ExtensionBuilder<T> withUiMode(int uiMode,
95                Supplier<T> supplier) {
96            mExtension.addUiMode(uiMode, supplier);
97            return this;
98        }
99
100        @Override
101        public ExtensionController.ExtensionBuilder<T> withCallback(
102                Consumer<T> callback) {
103            mExtension.mCallbacks.add(callback);
104            return this;
105        }
106
107        @Override
108        public ExtensionController.Extension build() {
109            // Manually sort, plugins first, tuners second, defaults last.
110            Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder));
111            mExtension.notifyChanged();
112            return mExtension;
113        }
114    }
115
116    private class ExtensionImpl<T> implements ExtensionController.Extension<T> {
117        private final ArrayList<Item<T>> mProducers = new ArrayList<>();
118        private final ArrayList<Consumer<T>> mCallbacks = new ArrayList<>();
119        private T mItem;
120        private Context mPluginContext;
121
122        public void addCallback(Consumer<T> callback) {
123            mCallbacks.add(callback);
124        }
125
126        @Override
127        public T get() {
128            return mItem;
129        }
130
131        @Override
132        public Context getContext() {
133            return mPluginContext != null ? mPluginContext : mDefaultContext;
134        }
135
136        @Override
137        public void destroy() {
138            for (int i = 0; i < mProducers.size(); i++) {
139                mProducers.get(i).destroy();
140            }
141        }
142
143        @Override
144        public T reload() {
145            notifyChanged();
146            return get();
147        }
148
149        private void notifyChanged() {
150            mItem = null;
151            for (int i = 0; i < mProducers.size(); i++) {
152                final T item = mProducers.get(i).get();
153                if (item != null) {
154                    mItem = item;
155                    break;
156                }
157            }
158            for (int i = 0; i < mCallbacks.size(); i++) {
159                mCallbacks.get(i).accept(mItem);
160            }
161        }
162
163        public void addDefault(Supplier<T> def) {
164            mProducers.add(new Default(def));
165        }
166
167        public <P> void addPlugin(String action, Class<P> cls, PluginConverter<T, P> converter) {
168            mProducers.add(new PluginItem(action, cls, converter));
169        }
170
171        public void addTunerFactory(TunerFactory<T> factory, String[] keys) {
172            mProducers.add(new TunerItem(factory, factory.keys()));
173        }
174
175        public void addUiMode(int uiMode, Supplier<T> mode) {
176            mProducers.add(new UiModeItem(uiMode, mode));
177        }
178
179        private class PluginItem<P extends Plugin> implements Item<T>, PluginListener<P> {
180            private final PluginConverter<T, P> mConverter;
181            private T mItem;
182
183            public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
184                mConverter = converter;
185                Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
186            }
187
188            @Override
189            public void onPluginConnected(P plugin, Context pluginContext) {
190                mPluginContext = pluginContext;
191                if (mConverter != null) {
192                    mItem = mConverter.getInterfaceFromPlugin(plugin);
193                } else {
194                    mItem = (T) plugin;
195                }
196                notifyChanged();
197            }
198
199            @Override
200            public void onPluginDisconnected(P plugin) {
201                mPluginContext = null;
202                mItem = null;
203                notifyChanged();
204            }
205
206            @Override
207            public T get() {
208                return mItem;
209            }
210
211            @Override
212            public void destroy() {
213                Dependency.get(PluginManager.class).removePluginListener(this);
214            }
215
216            @Override
217            public int sortOrder() {
218                return SORT_ORDER_PLUGIN;
219            }
220        }
221
222        private class TunerItem<T> implements Item<T>, Tunable {
223            private final TunerFactory<T> mFactory;
224            private final ArrayMap<String, String> mSettings = new ArrayMap<>();
225            private T mItem;
226
227            public TunerItem(TunerFactory<T> factory, String... setting) {
228                mFactory = factory;
229                Dependency.get(TunerService.class).addTunable(this, setting);
230            }
231
232            @Override
233            public T get() {
234                return mItem;
235            }
236
237            @Override
238            public void destroy() {
239                Dependency.get(TunerService.class).removeTunable(this);
240            }
241
242            @Override
243            public void onTuningChanged(String key, String newValue) {
244                mSettings.put(key, newValue);
245                mItem = mFactory.create(mSettings);
246                notifyChanged();
247            }
248
249            @Override
250            public int sortOrder() {
251                return SORT_ORDER_TUNER;
252            }
253        }
254
255        private class UiModeItem<T> implements Item<T>, ConfigurationListener {
256
257            private final int mDesiredUiMode;
258            private final Supplier<T> mSupplier;
259            private int mUiMode;
260            private Handler mHandler = new Handler();
261
262            public UiModeItem(int uiMode, Supplier<T> supplier) {
263                mDesiredUiMode = uiMode;
264                mSupplier = supplier;
265                mUiMode = mDefaultContext.getResources().getConfiguration().uiMode;
266                Dependency.get(ConfigurationController.class).addCallback(this);
267            }
268
269            @Override
270            public void onConfigChanged(Configuration newConfig) {
271                int newMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
272                if (newMode != mUiMode) {
273                    mUiMode = newMode;
274                    // Post to make sure we don't have concurrent modifications.
275                    mHandler.post(ExtensionImpl.this::notifyChanged);
276                }
277            }
278
279            @Override
280            public T get() {
281                return (mUiMode == mDesiredUiMode) ? mSupplier.get() : null;
282            }
283
284            @Override
285            public void destroy() {
286                Dependency.get(ConfigurationController.class).removeCallback(this);
287            }
288
289            @Override
290            public int sortOrder() {
291                return SORT_ORDER_UI_MODE;
292            }
293        }
294
295        private class Default<T> implements Item<T> {
296            private final Supplier<T> mSupplier;
297
298            public Default(Supplier<T> supplier) {
299                mSupplier = supplier;
300            }
301
302            @Override
303            public T get() {
304                return mSupplier.get();
305            }
306
307            @Override
308            public void destroy() {
309
310            }
311
312            @Override
313            public int sortOrder() {
314                return SORT_ORDER_DEFAULT;
315            }
316        }
317    }
318
319    private interface Item<T> extends Producer<T> {
320        int sortOrder();
321    }
322}
323