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.ConfigurationChangedReceiver;
24 import com.android.systemui.Dumpable;
25 import com.android.systemui.SystemUIFactory;
26 import com.android.systemui.qs.QSFragment;
27 import com.android.systemui.statusbar.phone.NavigationBarFragment;
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 ConfigurationChangedReceiver, 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     @Inject
FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent)54     public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) {
55         mFragmentCreator = rootComponent.createFragmentCreator();
56         initInjectionMap();
57     }
58 
getInjectionMap()59     ArrayMap<String, Method> getInjectionMap() {
60         return mInjectionMap;
61     }
62 
getFragmentCreator()63     FragmentCreator getFragmentCreator() {
64         return mFragmentCreator;
65     }
66 
initInjectionMap()67     private void initInjectionMap() {
68         for (Method method : FragmentCreator.class.getDeclaredMethods()) {
69             if (Fragment.class.isAssignableFrom(method.getReturnType())
70                     && (method.getModifiers() & Modifier.PUBLIC) != 0) {
71                 mInjectionMap.put(method.getReturnType().getName(), method);
72             }
73         }
74     }
75 
getFragmentHostManager(View view)76     public FragmentHostManager getFragmentHostManager(View view) {
77         View root = view.getRootView();
78         FragmentHostState state = mHosts.get(root);
79         if (state == null) {
80             state = new FragmentHostState(root);
81             mHosts.put(root, state);
82         }
83         return state.getFragmentHostManager();
84     }
85 
removeAndDestroy(View view)86     public void removeAndDestroy(View view) {
87         final FragmentHostState state = mHosts.remove(view.getRootView());
88         if (state != null) {
89             state.mFragmentHostManager.destroy();
90         }
91     }
92 
destroyAll()93     public void destroyAll() {
94         for (FragmentHostState state : mHosts.values()) {
95             state.mFragmentHostManager.destroy();
96         }
97     }
98 
99     @Override
onConfigurationChanged(Configuration newConfig)100     public void onConfigurationChanged(Configuration newConfig) {
101         for (FragmentHostState state : mHosts.values()) {
102             state.sendConfigurationChange(newConfig);
103         }
104     }
105 
106     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)107     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
108         pw.println("Dumping fragments:");
109         for (FragmentHostState state : mHosts.values()) {
110             state.mFragmentHostManager.getFragmentManager().dump("  ", fd, pw, args);
111         }
112     }
113 
114     /**
115      * The subcomponent of dagger that holds all fragments that need injection.
116      */
117     @Subcomponent
118     public interface FragmentCreator {
119         /**
120          * Inject a NavigationBarFragment.
121          */
createNavigationBarFragment()122         NavigationBarFragment createNavigationBarFragment();
123         /**
124          * Inject a QSFragment.
125          */
createQSFragment()126         QSFragment createQSFragment();
127     }
128 
129     private class FragmentHostState {
130         private final View mView;
131 
132         private FragmentHostManager mFragmentHostManager;
133 
FragmentHostState(View view)134         public FragmentHostState(View view) {
135             mView = view;
136             mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView);
137         }
138 
sendConfigurationChange(Configuration newConfig)139         public void sendConfigurationChange(Configuration newConfig) {
140             mHandler.post(() -> handleSendConfigurationChange(newConfig));
141         }
142 
getFragmentHostManager()143         public FragmentHostManager getFragmentHostManager() {
144             return mFragmentHostManager;
145         }
146 
handleSendConfigurationChange(Configuration newConfig)147         private void handleSendConfigurationChange(Configuration newConfig) {
148             mFragmentHostManager.onConfigurationChanged(newConfig);
149         }
150     }
151 }
152