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