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.SysUISingleton; 25 import com.android.systemui.qs.QSFragment; 26 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; 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 36 import dagger.Subcomponent; 37 38 /** 39 * Holds a map of root views to FragmentHostStates and generates them as needed. 40 * Also dispatches the configuration changes to all current FragmentHostStates. 41 */ 42 @SysUISingleton 43 public class FragmentService implements Dumpable { 44 45 private static final String TAG = "FragmentService"; 46 47 private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>(); 48 private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>(); 49 private final Handler mHandler = new Handler(); 50 private final FragmentCreator mFragmentCreator; 51 52 private ConfigurationController.ConfigurationListener mConfigurationListener = 53 new ConfigurationController.ConfigurationListener() { 54 @Override 55 public void onConfigChanged(Configuration newConfig) { 56 for (FragmentHostState state : mHosts.values()) { 57 state.sendConfigurationChange(newConfig); 58 } 59 } 60 }; 61 62 @Inject FragmentService(FragmentCreator.Factory fragmentCreatorFactory, ConfigurationController configurationController)63 public FragmentService(FragmentCreator.Factory fragmentCreatorFactory, 64 ConfigurationController configurationController) { 65 mFragmentCreator = fragmentCreatorFactory.build(); 66 initInjectionMap(); 67 configurationController.addCallback(mConfigurationListener); 68 } 69 getInjectionMap()70 ArrayMap<String, Method> getInjectionMap() { 71 return mInjectionMap; 72 } 73 getFragmentCreator()74 FragmentCreator getFragmentCreator() { 75 return mFragmentCreator; 76 } 77 initInjectionMap()78 private void initInjectionMap() { 79 for (Method method : FragmentCreator.class.getDeclaredMethods()) { 80 if (Fragment.class.isAssignableFrom(method.getReturnType()) 81 && (method.getModifiers() & Modifier.PUBLIC) != 0) { 82 mInjectionMap.put(method.getReturnType().getName(), method); 83 } 84 } 85 } 86 getFragmentHostManager(View view)87 public FragmentHostManager getFragmentHostManager(View view) { 88 View root = view.getRootView(); 89 FragmentHostState state = mHosts.get(root); 90 if (state == null) { 91 state = new FragmentHostState(root); 92 mHosts.put(root, state); 93 } 94 return state.getFragmentHostManager(); 95 } 96 removeAndDestroy(View view)97 public void removeAndDestroy(View view) { 98 final FragmentHostState state = mHosts.remove(view.getRootView()); 99 if (state != null) { 100 state.mFragmentHostManager.destroy(); 101 } 102 } 103 destroyAll()104 public void destroyAll() { 105 for (FragmentHostState state : mHosts.values()) { 106 state.mFragmentHostManager.destroy(); 107 } 108 } 109 110 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)111 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 112 pw.println("Dumping fragments:"); 113 for (FragmentHostState state : mHosts.values()) { 114 state.mFragmentHostManager.getFragmentManager().dump(" ", fd, pw, args); 115 } 116 } 117 118 /** 119 * The subcomponent of dagger that holds all fragments that need injection. 120 */ 121 @Subcomponent 122 public interface FragmentCreator { 123 /** Factory for creating a FragmentCreator. */ 124 @Subcomponent.Factory 125 interface Factory { build()126 FragmentCreator build(); 127 } 128 /** 129 * Inject a QSFragment. 130 */ createQSFragment()131 QSFragment createQSFragment(); 132 133 /** Inject a CollapsedStatusBarFragment. */ createCollapsedStatusBarFragment()134 CollapsedStatusBarFragment createCollapsedStatusBarFragment(); 135 } 136 137 private class FragmentHostState { 138 private final View mView; 139 140 private FragmentHostManager mFragmentHostManager; 141 FragmentHostState(View view)142 public FragmentHostState(View view) { 143 mView = view; 144 mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView); 145 } 146 sendConfigurationChange(Configuration newConfig)147 public void sendConfigurationChange(Configuration newConfig) { 148 mHandler.post(() -> handleSendConfigurationChange(newConfig)); 149 } 150 getFragmentHostManager()151 public FragmentHostManager getFragmentHostManager() { 152 return mFragmentHostManager; 153 } 154 handleSendConfigurationChange(Configuration newConfig)155 private void handleSendConfigurationChange(Configuration newConfig) { 156 mFragmentHostManager.onConfigurationChanged(newConfig); 157 } 158 } 159 } 160