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.dagger.SystemUIRootComponent;
30 import com.android.systemui.qs.QSFooterImpl;
31 import com.android.systemui.qs.QSPanel;
32 import com.android.systemui.qs.QuickQSPanel;
33 import com.android.systemui.qs.QuickStatusBarHeader;
34 import com.android.systemui.qs.customize.QSCustomizer;
35 import com.android.systemui.statusbar.NotificationShelf;
36 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
37 
38 import java.lang.reflect.InvocationTargetException;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Modifier;
41 
42 import javax.inject.Inject;
43 import javax.inject.Named;
44 import javax.inject.Singleton;
45 
46 import dagger.Module;
47 import dagger.Provides;
48 import dagger.Subcomponent;
49 
50 /**
51  * Manages inflation that requires dagger injection.
52  * See docs/dagger.md for details.
53  */
54 @Singleton
55 public class InjectionInflationController {
56 
57     public static final String VIEW_CONTEXT = "view_context";
58     private final ViewCreator mViewCreator;
59     private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
60     private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
61 
62     @Inject
InjectionInflationController(SystemUIRootComponent rootComponent)63     public InjectionInflationController(SystemUIRootComponent rootComponent) {
64         mViewCreator = rootComponent.createViewCreator();
65         initInjectionMap();
66     }
67 
getInjectionMap()68     ArrayMap<String, Method> getInjectionMap() {
69         return mInjectionMap;
70     }
71 
getFragmentCreator()72     ViewCreator getFragmentCreator() {
73         return mViewCreator;
74     }
75 
76     /**
77      * Wraps a {@link LayoutInflater} to support creating dagger injected views.
78      * See docs/dagger.md for details.
79      */
injectable(LayoutInflater inflater)80     public LayoutInflater injectable(LayoutInflater inflater) {
81         LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
82         ret.setPrivateFactory(mFactory);
83         return ret;
84     }
85 
initInjectionMap()86     private void initInjectionMap() {
87         for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
88             if (View.class.isAssignableFrom(method.getReturnType())
89                     && (method.getModifiers() & Modifier.PUBLIC) != 0) {
90                 mInjectionMap.put(method.getReturnType().getName(), method);
91             }
92         }
93     }
94 
95     /**
96      * The subcomponent of dagger that holds all views that need injection.
97      */
98     @Subcomponent
99     public interface ViewCreator {
100         /**
101          * Creates another subcomponent to actually generate the view.
102          */
createInstanceCreator(ViewAttributeProvider attributeProvider)103         ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
104     }
105 
106     /**
107      * Secondary sub-component that actually creates the views.
108      *
109      * Having two subcomponents lets us hide the complexity of providing the named context
110      * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
111      * creates a new ViewInstanceCreator any time we need to inflate a view.
112      */
113     @Subcomponent(modules = ViewAttributeProvider.class)
114     public interface ViewInstanceCreator {
115         /**
116          * Creates the QuickStatusBarHeader.
117          */
createQsHeader()118         QuickStatusBarHeader createQsHeader();
119         /**
120          * Creates the QSFooterImpl.
121          */
createQsFooter()122         QSFooterImpl createQsFooter();
123 
124         /**
125          * Creates the NotificationStackScrollLayout.
126          */
createNotificationStackScrollLayout()127         NotificationStackScrollLayout createNotificationStackScrollLayout();
128 
129         /**
130          * Creates the Shelf.
131          */
creatNotificationShelf()132         NotificationShelf creatNotificationShelf();
133 
134         /**
135          * Creates the KeyguardClockSwitch.
136          */
createKeyguardClockSwitch()137         KeyguardClockSwitch createKeyguardClockSwitch();
138 
139         /**
140          * Creates the KeyguardSliceView.
141          */
createKeyguardSliceView()142         KeyguardSliceView createKeyguardSliceView();
143 
144         /**
145          * Creates the KeyguardMessageArea.
146          */
createKeyguardMessageArea()147         KeyguardMessageArea createKeyguardMessageArea();
148 
149         /**
150          * Creates the QSPanel.
151          */
createQSPanel()152         QSPanel createQSPanel();
153 
154         /**
155          * Creates the QuickQSPanel.
156          */
createQuickQSPanel()157         QuickQSPanel createQuickQSPanel();
158 
159         /**
160          * Creates the QSCustomizer.
161          */
createQSCustomizer()162         QSCustomizer createQSCustomizer();
163     }
164 
165     /**
166      * Module for providing view-specific constructor objects.
167      */
168     @Module
169     public class ViewAttributeProvider {
170         private final Context mContext;
171         private final AttributeSet mAttrs;
172 
ViewAttributeProvider(Context context, AttributeSet attrs)173         private ViewAttributeProvider(Context context, AttributeSet attrs) {
174             mContext = context;
175             mAttrs = attrs;
176         }
177 
178         /**
179          * Provides the view-themed context (as opposed to the global sysui application context).
180          */
181         @Provides
182         @Named(VIEW_CONTEXT)
provideContext()183         public Context provideContext() {
184             return mContext;
185         }
186 
187         /**
188          * Provides the AttributeSet for the current view being inflated.
189          */
190         @Provides
provideAttributeSet()191         public AttributeSet provideAttributeSet() {
192             return mAttrs;
193         }
194     }
195 
196     private class InjectionFactory implements LayoutInflater.Factory2 {
197 
198         @Override
onCreateView(String name, Context context, AttributeSet attrs)199         public View onCreateView(String name, Context context, AttributeSet attrs) {
200             Method creationMethod = mInjectionMap.get(name);
201             if (creationMethod != null) {
202                 ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
203                 try {
204                     return (View) creationMethod.invoke(
205                             mViewCreator.createInstanceCreator(provider));
206                 } catch (IllegalAccessException e) {
207                     throw new InflateException("Could not inflate " + name, e);
208                 } catch (InvocationTargetException e) {
209                     throw new InflateException("Could not inflate " + name, e);
210                 }
211             }
212             return null;
213         }
214 
215         @Override
onCreateView(View parent, String name, Context context, AttributeSet attrs)216         public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
217             return onCreateView(name, context, attrs);
218         }
219     }
220 }
221