From 20ff3f9255c86a3747aa55746788d4bde1d12753 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Mon, 9 Jan 2017 15:13:23 -0500 Subject: [PATCH] SysUI fragments: Integrate new support for constructing Use a new system for constructing fragments so they can be swapped out in place maintaining state. This will allow easier integration with plugin lifecycle as parents who have child plugin fragments can depend on the class existing and won't have to listen to the lifecycle. Test: runtest systemui Change-Id: I517f4ce3d114abd49b1b5baca388d19e929b8f90 --- .../android/systemui/plugins/PluginFragment.java | 38 +------------ .../systemui/plugins/PluginInstanceManager.java | 8 ++- .../android/systemui/plugins/PluginListener.java | 4 +- .../android/systemui/PluginInflateContainer.java | 2 +- .../com/android/systemui/SystemUIApplication.java | 2 +- .../systemui/fragments/FragmentHostManager.java | 65 ++++++++++++++++++++++ .../systemui/fragments/PluginFragmentListener.java | 34 ++++------- .../statusbar/phone/KeyguardBottomAreaView.java | 4 +- .../statusbar/phone/NavigationBarInflaterView.java | 2 +- .../statusbar/phone/NavigationBarView.java | 2 +- .../systemui/statusbar/phone/PhoneStatusBar.java | 5 +- .../plugins/PluginInstanceManagerTest.java | 14 ++--- 12 files changed, 102 insertions(+), 78 deletions(-) diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java index a9d1fa94cf10..152dbc5e06be 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java @@ -14,17 +14,13 @@ package com.android.systemui.plugins; -import android.annotation.Nullable; import android.app.Fragment; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.view.LayoutInflater; public abstract class PluginFragment extends Fragment implements Plugin { - private static final String KEY_PLUGIN_PACKAGE = "plugin_package_name"; private Context mPluginContext; @Override @@ -33,45 +29,17 @@ public abstract class PluginFragment extends Fragment implements Plugin { } @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - Context sysuiContext = getContext(); - Context pluginContext = recreatePluginContext(sysuiContext, savedInstanceState); - onCreate(sysuiContext, pluginContext); - } - if (mPluginContext == null) { - throw new RuntimeException("PluginFragments must call super.onCreate(" - + "Context sysuiContext, Context pluginContext)"); - } + public LayoutInflater getLayoutInflater(Bundle savedInstanceState) { + return super.getLayoutInflater(savedInstanceState).cloneInContext(getContext()); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(KEY_PLUGIN_PACKAGE, getContext().getPackageName()); - } - - private Context recreatePluginContext(Context sysuiContext, Bundle savedInstanceState) { - final String pkg = savedInstanceState.getString(KEY_PLUGIN_PACKAGE); - try { - ApplicationInfo appInfo = sysuiContext.getPackageManager().getApplicationInfo(pkg, 0); - return PluginManager.getInstance(sysuiContext).getContext(appInfo, pkg); - } catch (NameNotFoundException e) { - throw new RuntimeException("Plugin with invalid package? " + pkg, e); - } - } - - @Override - public LayoutInflater getLayoutInflater(Bundle savedInstanceState) { - return super.getLayoutInflater(savedInstanceState).cloneInContext(mPluginContext); } - /** - * Should only be called after {@link Plugin#onCreate(Context, Context)}. - */ @Override public Context getContext() { - return mPluginContext != null ? mPluginContext : super.getContext(); + return mPluginContext; } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java index 47b97bdc29e7..9f44bd4bc331 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java @@ -177,8 +177,12 @@ public class PluginInstanceManager { if (DEBUG) Log.d(TAG, "onPluginConnected"); PluginPrefs.setHasPlugins(mContext); PluginInfo info = (PluginInfo) msg.obj; - info.mPlugin.onCreate(mContext, info.mPluginContext); - mListener.onPluginConnected(info.mPlugin); + if (!(msg.obj instanceof PluginFragment)) { + // Only call onDestroy for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + info.mPlugin.onCreate(mContext, info.mPluginContext); + } + mListener.onPluginConnected(info.mPlugin, info.mPluginContext); break; case PLUGIN_DISCONNECTED: if (DEBUG) Log.d(TAG, "onPluginDisconnected"); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java index b2f92d6c017e..b488d2a84baa 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java @@ -14,6 +14,8 @@ package com.android.systemui.plugins; +import android.content.Context; + /** * Interface for listening to plugins being connected. */ @@ -24,7 +26,7 @@ public interface PluginListener { * It may also be called in the future if the plugin package changes * and needs to be reloaded. */ - void onPluginConnected(T plugin); + void onPluginConnected(T plugin, Context pluginContext); /** * Called when a plugin has been uninstalled/updated and should be removed diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java index efa7cae0821d..efddf206878e 100644 --- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java +++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java @@ -98,7 +98,7 @@ public class PluginInflateContainer extends AutoReinflateContainer } @Override - public void onPluginConnected(ViewProvider plugin) { + public void onPluginConnected(ViewProvider plugin, Context context) { mPluginView = plugin.getView(); inflateLayout(); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index f83a5d3737de..f2aaec1dcfad 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -207,7 +207,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv PluginManager.getInstance(this).addPluginListener(OverlayPlugin.ACTION, new PluginListener() { @Override - public void onPluginConnected(OverlayPlugin plugin) { + public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class); if (phoneStatusBar != null) { plugin.setup(phoneStatusBar.getStatusBarWindow(), diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 57857ccb8cc6..50506a95851c 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -27,11 +27,13 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Parcelable; +import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.View; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.SystemUIApplication; +import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginManager; import java.io.FileDescriptor; @@ -47,6 +49,7 @@ public class FragmentHostManager { private final View mRootView; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(); private final FragmentService mManager; + private final PluginFragmentManager mPlugins = new PluginFragmentManager(); private FragmentController mFragments; private FragmentLifecycleCallbacks mLifecycleCallbacks; @@ -163,6 +166,10 @@ public class FragmentHostManager { return mFragments.getFragmentManager(); } + PluginFragmentManager getPluginManager() { + return mPlugins; + } + public interface FragmentListener { void onFragmentViewCreated(String tag, Fragment fragment); @@ -198,6 +205,11 @@ public class FragmentHostManager { } @Override + public Fragment instantiate(Context context, String className, Bundle arguments) { + return mPlugins.instantiate(context, className, arguments); + } + + @Override public boolean onShouldSaveFragmentState(Fragment fragment) { return true; // True for now. } @@ -237,4 +249,57 @@ public class FragmentHostManager { return true; } } + + class PluginFragmentManager { + private final ArrayMap mPluginLookup = new ArrayMap<>(); + + public void removePlugin(String tag, String currentClass, String defaultClass) { + Fragment fragment = getFragmentManager().findFragmentByTag(tag); + mPluginLookup.remove(currentClass); + getFragmentManager().beginTransaction() + .replace(((View) fragment.getView().getParent()).getId(), + instantiate(mContext, defaultClass, null), tag) + .commit(); + reloadFragments(); + } + + public void setCurrentPlugin(String tag, String currentClass, Context context) { + Fragment fragment = getFragmentManager().findFragmentByTag(tag); + mPluginLookup.put(currentClass, context); + getFragmentManager().beginTransaction() + .replace(((View) fragment.getView().getParent()).getId(), + instantiate(context, currentClass, null), tag) + .commit(); + reloadFragments(); + } + + private void reloadFragments() { + // Save the old state. + Parcelable p = destroyFragmentHost(); + // Generate a new fragment host and restore its state. + createFragmentHost(p); + } + + Fragment instantiate(Context context, String className, Bundle arguments) { + Context pluginContext = mPluginLookup.get(className); + if (pluginContext != null) { + Fragment f = Fragment.instantiate(pluginContext, className, arguments); + if (f instanceof Plugin) { + ((Plugin) f).onCreate(mContext, pluginContext); + } + return f; + } + return Fragment.instantiate(context, className, arguments); + } + } + + private static class PluginState { + Context mContext; + String mCls; + + private PluginState(String cls, Context context) { + mCls = cls; + mContext = context; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java index e107fcd5ebae..2e6de4ac9e35 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java @@ -15,6 +15,7 @@ package com.android.systemui.fragments; import android.app.Fragment; +import android.content.Context; import android.util.Log; import android.view.View; @@ -30,27 +31,19 @@ public class PluginFragmentListener implements PluginListener { private final FragmentHostManager mFragmentHostManager; private final PluginManager mPluginManager; private final Class mDefaultClass; - private final int mId; - private final String mTag; private final Class mExpectedInterface; + private final String mTag; - public PluginFragmentListener(View view, String tag, int id, - Class defaultFragment, + public PluginFragmentListener(View view, String tag, Class defaultFragment, Class expectedInterface) { + mTag = tag; mFragmentHostManager = FragmentHostManager.get(view); mPluginManager = PluginManager.getInstance(view.getContext()); mExpectedInterface = expectedInterface; - mTag = tag; mDefaultClass = defaultFragment; - mId = id; } public void startListening(String action, int version) { - try { - setFragment(mDefaultClass.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e); - } mPluginManager.addPluginListener(action, this, version, false /* Only allow one */); } @@ -58,17 +51,13 @@ public class PluginFragmentListener implements PluginListener { mPluginManager.removePluginListener(this); } - private void setFragment(Fragment fragment) { - mFragmentHostManager.getFragmentManager().beginTransaction() - .replace(mId, fragment, mTag) - .commit(); - } - @Override - public void onPluginConnected(Plugin plugin) { + public void onPluginConnected(Plugin plugin, Context pluginContext) { try { mExpectedInterface.cast(plugin); - setFragment((Fragment) plugin); + Fragment.class.cast(plugin); + mFragmentHostManager.getPluginManager().setCurrentPlugin(mTag, + plugin.getClass().getName(), pluginContext); } catch (ClassCastException e) { Log.e(TAG, plugin.getClass().getName() + " must be a Fragment and implement " + mExpectedInterface.getName(), e); @@ -77,10 +66,7 @@ public class PluginFragmentListener implements PluginListener { @Override public void onPluginDisconnected(Plugin plugin) { - try { - setFragment(mDefaultClass.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e); - } + mFragmentHostManager.getPluginManager().removePlugin(mTag, + plugin.getClass().getName(), mDefaultClass.getName()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index d3267876994f..79120d889c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -779,7 +779,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private final PluginListener mRightListener = new PluginListener() { @Override - public void onPluginConnected(IntentButtonProvider plugin) { + public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) { setRightButton(plugin.getIntentButton()); } @@ -792,7 +792,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private final PluginListener mLeftListener = new PluginListener() { @Override - public void onPluginConnected(IntentButtonProvider plugin) { + public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) { setLeftButton(plugin.getIntentButton()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index b6feb0eba058..f04a9ee71404 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -365,7 +365,7 @@ public class NavigationBarInflaterView extends FrameLayout } @Override - public void onPluginConnected(NavBarButtonProvider plugin) { + public void onPluginConnected(NavBarButtonProvider plugin, Context context) { mPlugins.add(plugin); clearViews(); inflateLayout(mCurrentLayout); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 31c78c8f911d..319f124f0554 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -765,7 +765,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener