Merge "SysUI fragments: Integrate new support for constructing"
This commit is contained in:
committed by
Android (Google) Code Review
commit
19655fc019
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -197,6 +204,11 @@ public class FragmentHostManager {
|
||||
FragmentHostManager.this.dump(prefix, fd, writer, args);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user