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