Home | History | Annotate | Download | only in util
      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
     64     public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
     65         mViewCreator = rootComponent.createViewCreator();
     66         initInjectionMap();
     67     }
     68 
     69     ArrayMap<String, Method> getInjectionMap() {
     70         return mInjectionMap;
     71     }
     72 
     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      */
     81     public LayoutInflater injectable(LayoutInflater inflater) {
     82         LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
     83         ret.setPrivateFactory(mFactory);
     84         return ret;
     85     }
     86 
     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          */
    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          */
    119         QuickStatusBarHeader createQsHeader();
    120         /**
    121          * Creates the QSFooterImpl.
    122          */
    123         QSFooterImpl createQsFooter();
    124 
    125         /**
    126          * Creates the NotificationStackScrollLayout.
    127          */
    128         NotificationStackScrollLayout createNotificationStackScrollLayout();
    129 
    130         /**
    131          * Creates the NotificationPanelView.
    132          */
    133         NotificationPanelView createPanelView();
    134 
    135         /**
    136          * Creates the QSCarrierGroup
    137          */
    138         QSCarrierGroup createQSCarrierGroup();
    139 
    140         /**
    141          * Creates the KeyguardClockSwitch.
    142          */
    143         KeyguardClockSwitch createKeyguardClockSwitch();
    144 
    145         /**
    146          * Creates the KeyguardSliceView.
    147          */
    148         KeyguardSliceView createKeyguardSliceView();
    149 
    150         /**
    151          * Creates the KeyguardMessageArea.
    152          */
    153         KeyguardMessageArea createKeyguardMessageArea();
    154 
    155         /**
    156          * Creates the keyguard LockIcon.
    157          */
    158         LockIcon createLockIcon();
    159 
    160         /**
    161          * Creates the QSPanel.
    162          */
    163         QSPanel createQSPanel();
    164 
    165         /**
    166          * Creates the QuickQSPanel.
    167          */
    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 
    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)
    189         public Context provideContext() {
    190             return mContext;
    191         }
    192 
    193         /**
    194          * Provides the AttributeSet for the current view being inflated.
    195          */
    196         @Provides
    197         public AttributeSet provideAttributeSet() {
    198             return mAttrs;
    199         }
    200     }
    201 
    202     private class InjectionFactory implements LayoutInflater.Factory2 {
    203 
    204         @Override
    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
    222         public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    223             return onCreateView(name, context, attrs);
    224         }
    225     }
    226 }
    227