/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.util; import android.content.Context; import android.util.ArrayMap; import android.util.AttributeSet; import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSliceView; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import dagger.Subcomponent; /** * Manages inflation that requires dagger injection. * See docs/dagger.md for details. */ @Singleton public class InjectionInflationController { public static final String VIEW_CONTEXT = "view_context"; private final ViewCreator mViewCreator; private final ArrayMap mInjectionMap = new ArrayMap<>(); private final LayoutInflater.Factory2 mFactory = new InjectionFactory(); @Inject public InjectionInflationController(SystemUIRootComponent rootComponent) { mViewCreator = rootComponent.createViewCreator(); initInjectionMap(); } ArrayMap getInjectionMap() { return mInjectionMap; } ViewCreator getFragmentCreator() { return mViewCreator; } /** * Wraps a {@link LayoutInflater} to support creating dagger injected views. * See docs/dagger.md for details. */ public LayoutInflater injectable(LayoutInflater inflater) { LayoutInflater ret = inflater.cloneInContext(inflater.getContext()); ret.setPrivateFactory(mFactory); return ret; } private void initInjectionMap() { for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) { if (View.class.isAssignableFrom(method.getReturnType()) && (method.getModifiers() & Modifier.PUBLIC) != 0) { mInjectionMap.put(method.getReturnType().getName(), method); } } } /** * The subcomponent of dagger that holds all views that need injection. */ @Subcomponent public interface ViewCreator { /** * Creates another subcomponent to actually generate the view. */ ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider); } /** * Secondary sub-component that actually creates the views. * * Having two subcomponents lets us hide the complexity of providing the named context * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that * creates a new ViewInstanceCreator any time we need to inflate a view. */ @Subcomponent(modules = ViewAttributeProvider.class) public interface ViewInstanceCreator { /** * Creates the QuickStatusBarHeader. */ QuickStatusBarHeader createQsHeader(); /** * Creates the QSFooterImpl. */ QSFooterImpl createQsFooter(); /** * Creates the NotificationStackScrollLayout. */ NotificationStackScrollLayout createNotificationStackScrollLayout(); /** * Creates the Shelf. */ NotificationShelf creatNotificationShelf(); /** * Creates the KeyguardClockSwitch. */ KeyguardClockSwitch createKeyguardClockSwitch(); /** * Creates the KeyguardSliceView. */ KeyguardSliceView createKeyguardSliceView(); /** * Creates the KeyguardMessageArea. */ KeyguardMessageArea createKeyguardMessageArea(); /** * Creates the QSPanel. */ QSPanel createQSPanel(); /** * Creates the QuickQSPanel. */ QuickQSPanel createQuickQSPanel(); /** * Creates the QSCustomizer. */ QSCustomizer createQSCustomizer(); } /** * Module for providing view-specific constructor objects. */ @Module public class ViewAttributeProvider { private final Context mContext; private final AttributeSet mAttrs; private ViewAttributeProvider(Context context, AttributeSet attrs) { mContext = context; mAttrs = attrs; } /** * Provides the view-themed context (as opposed to the global sysui application context). */ @Provides @Named(VIEW_CONTEXT) public Context provideContext() { return mContext; } /** * Provides the AttributeSet for the current view being inflated. */ @Provides public AttributeSet provideAttributeSet() { return mAttrs; } } private class InjectionFactory implements LayoutInflater.Factory2 { @Override public View onCreateView(String name, Context context, AttributeSet attrs) { Method creationMethod = mInjectionMap.get(name); if (creationMethod != null) { ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs); try { return (View) creationMethod.invoke( mViewCreator.createInstanceCreator(provider)); } catch (IllegalAccessException e) { throw new InflateException("Could not inflate " + name, e); } catch (InvocationTargetException e) { throw new InflateException("Could not inflate " + name, e); } } return null; } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { return onCreateView(name, context, attrs); } } }