diff options
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<T extends Plugin> { if (DEBUG) Log.d(TAG, "onPluginConnected"); PluginPrefs.setHasPlugins(mContext); PluginInfo<T> info = (PluginInfo<T>) 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<T extends Plugin> { * 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<OverlayPlugin>() { @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<String, Context> 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<Plugin> { private final FragmentHostManager mFragmentHostManager; private final PluginManager mPluginManager; private final Class<? extends Fragment> mDefaultClass; - private final int mId; - private final String mTag; private final Class<? extends FragmentBase> mExpectedInterface; + private final String mTag; - public PluginFragmentListener(View view, String tag, int id, - Class<? extends Fragment> defaultFragment, + public PluginFragmentListener(View view, String tag, Class<? extends Fragment> defaultFragment, Class<? extends FragmentBase> 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<Plugin> { 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<Plugin> { @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<IntentButtonProvider> mRightListener = new PluginListener<IntentButtonProvider>() { @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<IntentButtonProvider> mLeftListener = new PluginListener<IntentButtonProvider>() { @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<Nav } @Override - public void onPluginConnected(NavGesture plugin) { + public void onPluginConnected(NavGesture plugin, Context context) { mGestureHelper = plugin.getGestureHelper(); updateTaskSwitchHelper(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index cb9fbb969446..80ad9d2d780a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -870,7 +870,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, View container = mStatusBarWindow.findViewById(R.id.qs_frame); if (container != null) { FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); - new PluginFragmentListener(container, QS.TAG, R.id.qs_frame, QSFragment.class, QS.class) + fragmentHostManager.getFragmentManager().beginTransaction() + .replace(R.id.qs_frame, new QSFragment(), QS.TAG) + .commit(); + new PluginFragmentListener(container, QS.TAG, QSFragment.class, QS.class) .startListening(QS.ACTION, QS.VERSION); final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mIconController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java index d529ee105dd9..3715df2a42c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java @@ -113,8 +113,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { waitForIdleSync(mPluginInstanceManager.mPluginHandler); waitForIdleSync(mPluginInstanceManager.mMainHandler); - verify(mMockListener, Mockito.never()).onPluginConnected( - ArgumentCaptor.forClass(Plugin.class).capture()); + verify(mMockListener, Mockito.never()).onPluginConnected(any(), any()); } @Test @@ -124,7 +123,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { // Verify startup lifecycle verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), ArgumentCaptor.forClass(Context.class).capture()); - verify(mMockListener).onPluginConnected(ArgumentCaptor.forClass(Plugin.class).capture()); + verify(mMockListener).onPluginConnected(any(), any()); } @Test @@ -154,8 +153,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { waitForIdleSync(mPluginInstanceManager.mMainHandler); // Plugin shouldn't be connected because it is the wrong version. - verify(mMockListener, Mockito.never()).onPluginConnected( - ArgumentCaptor.forClass(Plugin.class).capture()); + verify(mMockListener, Mockito.never()).onPluginConnected(any(), any()); verify(nm).notifyAsUser(eq(TestPlugin.class.getName()), eq(SystemMessage.NOTE_PLUGIN), any(), eq(UserHandle.ALL)); } @@ -176,8 +174,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { verify(sMockPlugin, Mockito.times(2)).onCreate( ArgumentCaptor.forClass(Context.class).capture(), ArgumentCaptor.forClass(Context.class).capture()); - verify(mMockListener, Mockito.times(2)).onPluginConnected( - ArgumentCaptor.forClass(Plugin.class).capture()); + verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any()); } @Test @@ -193,8 +190,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { waitForIdleSync(mPluginInstanceManager.mMainHandler);; // Non-debuggable build should receive no plugins. - verify(mMockListener, Mockito.never()).onPluginConnected( - ArgumentCaptor.forClass(Plugin.class).capture()); + verify(mMockListener, Mockito.never()).onPluginConnected(any(), any()); } @Test |