1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.util;
18 
19 import android.content.Context;
20 import android.util.ArrayMap;
21 import android.util.AttributeSet;
22 import android.view.InflateException;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 
26 import com.android.keyguard.KeyguardClockSwitch;
27 import com.android.keyguard.KeyguardMessageArea;
28 import com.android.keyguard.KeyguardSliceView;
29 import com.android.systemui.SystemUIRootComponent;
30 import com.android.systemui.qs.QSCarrierGroup;
31 import com.android.systemui.qs.QSFooterImpl;
32 import com.android.systemui.qs.QSPanel;
33 import com.android.systemui.qs.QuickQSPanel;
34 import com.android.systemui.qs.QuickStatusBarHeader;
35 import com.android.systemui.qs.customize.QSCustomizer;
36 import com.android.systemui.statusbar.NotificationShelf;
37 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
38 import com.android.systemui.statusbar.phone.LockIcon;
39 import com.android.systemui.statusbar.phone.NotificationPanelView;
40 
41 import java.lang.reflect.InvocationTargetException;
42 import java.lang.reflect.Method;
43 import java.lang.reflect.Modifier;
44 
45 import javax.inject.Inject;
46 import javax.inject.Named;
47 import javax.inject.Singleton;
48 
49 import dagger.Module;
50 import dagger.Provides;
51 import dagger.Subcomponent;
52 
53 /**
54  * Manages inflation that requires dagger injection.
55  * See docs/dagger.md for details.
56  */
57 @Singleton
58 public class InjectionInflationController {
59 
60     public static final String VIEW_CONTEXT = "view_context";
61     private final ViewCreator mViewCreator;
62     private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
63     private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
64 
65     @Inject
InjectionInflationController(SystemUIRootComponent rootComponent)66     public InjectionInflationController(SystemUIRootComponent rootComponent) {
67         mViewCreator = rootComponent.createViewCreator();
68         initInjectionMap();
69     }
70 
getInjectionMap()71     ArrayMap<String, Method> getInjectionMap() {
72         return mInjectionMap;
73     }
74 
getFragmentCreator()75     ViewCreator getFragmentCreator() {
76         return mViewCreator;
77     }
78 
79     /**
80      * Wraps a {@link LayoutInflater} to support creating dagger injected views.
81      * See docs/dagger.md for details.
82      */
injectable(LayoutInflater inflater)83     public LayoutInflater injectable(LayoutInflater inflater) {
84         LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
85         ret.setPrivateFactory(mFactory);
86         return ret;
87     }
88 
initInjectionMap()89     private void initInjectionMap() {
90         for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
91             if (View.class.isAssignableFrom(method.getReturnType())
92                     && (method.getModifiers() & Modifier.PUBLIC) != 0) {
93                 mInjectionMap.put(method.getReturnType().getName(), method);
94             }
95         }
96     }
97 
98     /**
99      * The subcomponent of dagger that holds all views that need injection.
100      */
101     @Subcomponent
102     public interface ViewCreator {
103         /**
104          * Creates another subcomponent to actually generate the view.
105          */
createInstanceCreator(ViewAttributeProvider attributeProvider)106         ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
107     }
108 
109     /**
110      * Secondary sub-component that actually creates the views.
111      *
112      * Having two subcomponents lets us hide the complexity of providing the named context
113      * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
114      * creates a new ViewInstanceCreator any time we need to inflate a view.
115      */
116     @Subcomponent(modules = ViewAttributeProvider.class)
117     public interface ViewInstanceCreator {
118         /**
119          * Creates the QuickStatusBarHeader.
120          */
createQsHeader()121         QuickStatusBarHeader createQsHeader();
122         /**
123          * Creates the QSFooterImpl.
124          */
createQsFooter()125         QSFooterImpl createQsFooter();
126 
127         /**
128          * Creates the NotificationStackScrollLayout.
129          */
createNotificationStackScrollLayout()130         NotificationStackScrollLayout createNotificationStackScrollLayout();
131 
132         /**
133          * Creates the NotificationPanelView.
134          */
createPanelView()135         NotificationPanelView createPanelView();
136 
137         /**
138          * Creates the QSCarrierGroup
139          */
createQSCarrierGroup()140         QSCarrierGroup createQSCarrierGroup();
141 
142         /**
143          * Creates the Shelf.
144          */
creatNotificationShelf()145         NotificationShelf creatNotificationShelf();
146 
147         /**
148          * Creates the KeyguardClockSwitch.
149          */
createKeyguardClockSwitch()150         KeyguardClockSwitch createKeyguardClockSwitch();
151 
152         /**
153          * Creates the KeyguardSliceView.
154          */
createKeyguardSliceView()155         KeyguardSliceView createKeyguardSliceView();
156 
157         /**
158          * Creates the KeyguardMessageArea.
159          */
createKeyguardMessageArea()160         KeyguardMessageArea createKeyguardMessageArea();
161 
162         /**
163          * Creates the keyguard LockIcon.
164          */
createLockIcon()165         LockIcon createLockIcon();
166 
167         /**
168          * Creates the QSPanel.
169          */
createQSPanel()170         QSPanel createQSPanel();
171 
172         /**
173          * Creates the QuickQSPanel.
174          */
createQuickQSPanel()175         QuickQSPanel createQuickQSPanel();
176 
177         /**
178          * Creates the QSCustomizer.
179          */
createQSCustomizer()180         QSCustomizer createQSCustomizer();
181     }
182 
183     /**
184      * Module for providing view-specific constructor objects.
185      */
186     @Module
187     public class ViewAttributeProvider {
188         private final Context mContext;
189         private final AttributeSet mAttrs;
190 
ViewAttributeProvider(Context context, AttributeSet attrs)191         private ViewAttributeProvider(Context context, AttributeSet attrs) {
192             mContext = context;
193             mAttrs = attrs;
194         }
195 
196         /**
197          * Provides the view-themed context (as opposed to the global sysui application context).
198          */
199         @Provides
200         @Named(VIEW_CONTEXT)
provideContext()201         public Context provideContext() {
202             return mContext;
203         }
204 
205         /**
206          * Provides the AttributeSet for the current view being inflated.
207          */
208         @Provides
provideAttributeSet()209         public AttributeSet provideAttributeSet() {
210             return mAttrs;
211         }
212     }
213 
214     private class InjectionFactory implements LayoutInflater.Factory2 {
215 
216         @Override
onCreateView(String name, Context context, AttributeSet attrs)217         public View onCreateView(String name, Context context, AttributeSet attrs) {
218             Method creationMethod = mInjectionMap.get(name);
219             if (creationMethod != null) {
220                 ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
221                 try {
222                     return (View) creationMethod.invoke(
223                             mViewCreator.createInstanceCreator(provider));
224                 } catch (IllegalAccessException e) {
225                     throw new InflateException("Could not inflate " + name, e);
226                 } catch (InvocationTargetException e) {
227                     throw new InflateException("Could not inflate " + name, e);
228                 }
229             }
230             return null;
231         }
232 
233         @Override
onCreateView(View parent, String name, Context context, AttributeSet attrs)234         public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
235             return onCreateView(name, context, attrs);
236         }
237     }
238 }
239