summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java240
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java82
3 files changed, 324 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 99e787676ef5..359c33019600 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -29,6 +29,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyboard.KeyboardUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.media.RingtonePlayer;
@@ -61,6 +62,7 @@ public class SystemUIApplication extends Application {
* The classes of the stuff to start.
*/
private final Class<?>[] SERVICES = new Class[] {
+ FragmentService.class,
TunerService.class,
KeyguardViewMediator.class,
Recents.class,
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
new file mode 100644
index 000000000000..634e5974a39e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 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.fragments;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
+import android.app.FragmentManager;
+import android.app.FragmentManager.FragmentLifecycleCallbacks;
+import android.app.FragmentManagerNonConfig;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.SystemUIApplication;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class FragmentHostManager {
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Context mContext;
+ private final HashMap<String, ArrayList<FragmentListener>> mListeners = new HashMap<>();
+ private final View mRootView;
+ private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges();
+ private final FragmentService mManager;
+
+ private FragmentController mFragments;
+ private FragmentLifecycleCallbacks mLifecycleCallbacks;
+
+ FragmentHostManager(Context context, FragmentService manager, View rootView) {
+ mContext = context;
+ mManager = manager;
+ mRootView = rootView;
+ mConfigChanges.applyNewConfig(context.getResources());
+ createFragmentHost(null);
+ }
+
+ private void createFragmentHost(Parcelable savedState) {
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ // TODO: Remove non-staticness from FragmentLifecycleCallbacks (hopefully).
+ mLifecycleCallbacks = mFragments.getFragmentManager().new FragmentLifecycleCallbacks() {
+ @Override
+ public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
+ Bundle savedInstanceState) {
+ FragmentHostManager.this.onFragmentViewCreated(f);
+ }
+
+ @Override
+ public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
+ FragmentHostManager.this.onFragmentViewDestroyed(f);
+ }
+ };
+ mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,
+ true);
+ if (savedState != null) {
+ mFragments.restoreAllState(savedState, (FragmentManagerNonConfig) null);
+ }
+ // For now just keep all fragments in the resumed state.
+ mFragments.dispatchCreate();
+ mFragments.dispatchStart();
+ mFragments.dispatchResume();
+ }
+
+ private Parcelable destroyFragmentHost() {
+ mFragments.dispatchPause();
+ Parcelable p = mFragments.saveAllState();
+ mFragments.dispatchStop();
+ mFragments.dispatchDestroy();
+ mFragments.getFragmentManager().unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks);
+ return p;
+ }
+
+ public void addTagListener(String tag, FragmentListener listener) {
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners == null) {
+ listeners = new ArrayList<>();
+ mListeners.put(tag, listeners);
+ }
+ listeners.add(listener);
+ Fragment current = getFragmentManager().findFragmentByTag(tag);
+ if (current != null && current.getView() != null) {
+ listener.onFragmentViewCreated(tag, current);
+ }
+ }
+
+ // Shouldn't generally be needed, included for completeness sake.
+ public void removeTagListener(String tag, FragmentListener listener) {
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null && listeners.remove(listener) && listeners.size() == 0) {
+ mListeners.remove(tag);
+ }
+ }
+
+ private void onFragmentViewCreated(Fragment fragment) {
+ String tag = fragment.getTag();
+
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null) {
+ listeners.forEach((listener) -> listener.onFragmentViewCreated(tag, fragment));
+ }
+ }
+
+ private void onFragmentViewDestroyed(Fragment fragment) {
+ String tag = fragment.getTag();
+
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null) {
+ listeners.forEach((listener) -> listener.onFragmentViewDestroyed(tag, fragment));
+ }
+ }
+
+ /**
+ * Called when the configuration changed, return true if the fragments
+ * should be recreated.
+ */
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ // Save the old state.
+ Parcelable p = destroyFragmentHost();
+ // Generate a new fragment host and restore its state.
+ createFragmentHost(p);
+ } else {
+ mFragments.dispatchConfigurationChanged(newConfig);
+ }
+ }
+
+ private void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ // TODO: Do something?
+ }
+
+ private View findViewById(int id) {
+ return mRootView.findViewById(id);
+ }
+
+ /**
+ * Note: Values from this shouldn't be cached as they can change after config changes.
+ */
+ public FragmentManager getFragmentManager() {
+ return mFragments.getFragmentManager();
+ }
+
+ public interface FragmentListener {
+ void onFragmentViewCreated(String tag, Fragment fragment);
+
+ // The facts of lifecycle
+ // When a fragment is destroyed, you should not talk to it any longer.
+ default void onFragmentViewDestroyed(String tag, Fragment fragment) {
+ }
+ }
+
+ public static FragmentHostManager get(View view) {
+ try {
+ return ((SystemUIApplication) view.getContext().getApplicationContext())
+ .getComponent(FragmentService.class).getFragmentHostManager(view);
+ } catch (ClassCastException e) {
+ // TODO: Some auto handling here?
+ throw e;
+ }
+ }
+
+ class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
+ public HostCallbacks() {
+ super(mContext, FragmentHostManager.this.mHandler, 0);
+ }
+
+ @Override
+ public FragmentHostManager onGetHost() {
+ return FragmentHostManager.this;
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ FragmentHostManager.this.dump(prefix, fd, writer, args);
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true; // True for now.
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public boolean onUseFragmentManagerInflaterFactory() {
+ return true;
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return false;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ return 0;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ }
+
+ @Override
+ @Nullable
+ public View onFindViewById(int id) {
+ return FragmentHostManager.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
new file mode 100644
index 000000000000..85cde10b5a53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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.fragments;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIApplication;
+
+/**
+ * Holds a map of root views to FragmentHostStates and generates them as needed.
+ * Also dispatches the configuration changes to all current FragmentHostStates.
+ */
+public class FragmentService extends SystemUI {
+
+ private static final String TAG = "FragmentService";
+
+ private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
+ private final Handler mHandler = new Handler();
+
+ @Override
+ public void start() {
+ putComponent(FragmentService.class, this);
+ }
+
+ public FragmentHostManager getFragmentHostManager(View view) {
+ View root = view.getRootView();
+ FragmentHostState state = mHosts.get(root);
+ if (state == null) {
+ state = new FragmentHostState(root);
+ mHosts.put(root, state);
+ }
+ return state.getFragmentHostManager();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ for (FragmentHostState state : mHosts.values()) {
+ state.sendConfigurationChange(newConfig);
+ }
+ }
+
+ private class FragmentHostState {
+ private final View mView;
+
+ private FragmentHostManager mFragmentHostManager;
+
+ public FragmentHostState(View view) {
+ mView = view;
+ mFragmentHostManager = new FragmentHostManager(mContext, FragmentService.this, mView);
+ }
+
+ public void sendConfigurationChange(Configuration newConfig) {
+ mHandler.post(() -> handleSendConfigurationChange(newConfig));
+ }
+
+ public FragmentHostManager getFragmentHostManager() {
+ return mFragmentHostManager;
+ }
+
+ private void handleSendConfigurationChange(Configuration newConfig) {
+ mFragmentHostManager.onConfigurationChanged(newConfig);
+ }
+ }
+}