1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.fragments;
16 
17 import android.app.Fragment;
18 import android.content.res.Configuration;
19 import android.os.Handler;
20 import android.util.ArrayMap;
21 import android.view.View;
22 
23 import com.android.systemui.Dumpable;
24 import com.android.systemui.dagger.SystemUIRootComponent;
25 import com.android.systemui.qs.QSFragment;
26 import com.android.systemui.statusbar.phone.NavigationBarFragment;
27 import com.android.systemui.statusbar.policy.ConfigurationController;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 
34 import javax.inject.Inject;
35 import javax.inject.Singleton;
36 
37 import dagger.Subcomponent;
38 
39 /**
40  * Holds a map of root views to FragmentHostStates and generates them as needed.
41  * Also dispatches the configuration changes to all current FragmentHostStates.
42  */
43 @Singleton
44 public class FragmentService implements Dumpable {
45 
46     private static final String TAG = "FragmentService";
47 
48     private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
49     private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
50     private final Handler mHandler = new Handler();
51     private final FragmentCreator mFragmentCreator;
52 
53     private ConfigurationController.ConfigurationListener mConfigurationListener =
54             new ConfigurationController.ConfigurationListener() {
55                 @Override
56                 public void onConfigChanged(Configuration newConfig) {
57                     for (FragmentHostState state : mHosts.values()) {
58                         state.sendConfigurationChange(newConfig);
59                     }
60                 }
61             };
62 
63     @Inject
FragmentService(SystemUIRootComponent rootComponent, ConfigurationController configurationController)64     public FragmentService(SystemUIRootComponent rootComponent,
65             ConfigurationController configurationController) {
66         mFragmentCreator = rootComponent.createFragmentCreator();
67         initInjectionMap();
68         configurationController.addCallback(mConfigurationListener);
69     }
70 
getInjectionMap()71     ArrayMap<String, Method> getInjectionMap() {
72         return mInjectionMap;
73     }
74 
getFragmentCreator()75     FragmentCreator getFragmentCreator() {
76         return mFragmentCreator;
77     }
78 
initInjectionMap()79     private void initInjectionMap() {
80         for (Method method : FragmentCreator.class.getDeclaredMethods()) {
81             if (Fragment.class.isAssignableFrom(method.getReturnType())
82                     && (method.getModifiers() & Modifier.PUBLIC) != 0) {
83                 mInjectionMap.put(method.getReturnType().getName(), method);
84             }
85         }
86     }
87 
getFragmentHostManager(View view)88     public FragmentHostManager getFragmentHostManager(View view) {
89         View root = view.getRootView();
90         FragmentHostState state = mHosts.get(root);
91         if (state == null) {
92             state = new FragmentHostState(root);
93             mHosts.put(root, state);
94         }
95         return state.getFragmentHostManager();
96     }
97 
removeAndDestroy(View view)98     public void removeAndDestroy(View view) {
99         final FragmentHostState state = mHosts.remove(view.getRootView());
100         if (state != null) {
101             state.mFragmentHostManager.destroy();
102         }
103     }
104 
destroyAll()105     public void destroyAll() {
106         for (FragmentHostState state : mHosts.values()) {
107             state.mFragmentHostManager.destroy();
108         }
109     }
110 
111     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)112     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
113         pw.println("Dumping fragments:");
114         for (FragmentHostState state : mHosts.values()) {
115             state.mFragmentHostManager.getFragmentManager().dump("  ", fd, pw, args);
116         }
117     }
118 
119     /**
120      * The subcomponent of dagger that holds all fragments that need injection.
121      */
122     @Subcomponent
123     public interface FragmentCreator {
124         /**
125          * Inject a NavigationBarFragment.
126          */
createNavigationBarFragment()127         NavigationBarFragment createNavigationBarFragment();
128         /**
129          * Inject a QSFragment.
130          */
createQSFragment()131         QSFragment createQSFragment();
132     }
133 
134     private class FragmentHostState {
135         private final View mView;
136 
137         private FragmentHostManager mFragmentHostManager;
138 
FragmentHostState(View view)139         public FragmentHostState(View view) {
140             mView = view;
141             mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView);
142         }
143 
sendConfigurationChange(Configuration newConfig)144         public void sendConfigurationChange(Configuration newConfig) {
145             mHandler.post(() -> handleSendConfigurationChange(newConfig));
146         }
147 
getFragmentHostManager()148         public FragmentHostManager getFragmentHostManager() {
149             return mFragmentHostManager;
150         }
151 
handleSendConfigurationChange(Configuration newConfig)152         private void handleSendConfigurationChange(Configuration newConfig) {
153             mFragmentHostManager.onConfigurationChanged(newConfig);
154         }
155     }
156 }
157