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