aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java693
1 files changed, 0 insertions, 693 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
deleted file mode 100644
index 020c666b9..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ProjectCallback.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
- * 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.ide.eclipse.adt.internal.editors.layout;
-
-import static com.android.SdkConstants.ANDROID_PKG_PREFIX;
-import static com.android.SdkConstants.CALENDAR_VIEW;
-import static com.android.SdkConstants.CLASS_VIEW;
-import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
-import static com.android.SdkConstants.FQCN_GRID_VIEW;
-import static com.android.SdkConstants.FQCN_SPINNER;
-import static com.android.SdkConstants.GRID_VIEW;
-import static com.android.SdkConstants.LIST_VIEW;
-import static com.android.SdkConstants.SPINNER;
-import static com.android.SdkConstants.VIEW_FRAGMENT;
-import static com.android.SdkConstants.VIEW_INCLUDE;
-
-import com.android.SdkConstants;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.RenderSecurityManager;
-import com.android.ide.common.rendering.api.ActionBarCallback;
-import com.android.ide.common.rendering.api.AdapterBinding;
-import com.android.ide.common.rendering.api.DataBindingItem;
-import com.android.ide.common.rendering.api.Features;
-import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.IProjectCallback;
-import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.xml.ManifestData;
-import com.android.ide.eclipse.adt.AdtConstants;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderLogger;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectClassLoader;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.resources.ResourceType;
-import com.android.util.Pair;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.eclipse.core.resources.IProject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.StringReader;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * Loader for Android Project class in order to use them in the layout editor.
- * <p/>This implements {@link IProjectCallback} for the old and new API through
- * {@link LayoutlibCallback}
- */
-public final class ProjectCallback extends LayoutlibCallback {
- private final HashMap<String, Class<?>> mLoadedClasses = new HashMap<String, Class<?>>();
- private final Set<String> mMissingClasses = new TreeSet<String>();
- private final Set<String> mBrokenClasses = new TreeSet<String>();
- private final IProject mProject;
- private final ClassLoader mParentClassLoader;
- private final ProjectResources mProjectRes;
- private final Object mCredential;
- private boolean mUsed = false;
- private String mNamespace;
- private ProjectClassLoader mLoader = null;
- private LayoutLog mLogger;
- private LayoutLibrary mLayoutLib;
- private String mLayoutName;
- private ILayoutPullParser mLayoutEmbeddedParser;
- private ResourceResolver mResourceResolver;
- private GraphicalEditorPart mEditor;
-
- /**
- * Creates a new {@link ProjectCallback} to be used with the layout lib.
- *
- * @param layoutLib The layout library this callback is going to be invoked from
- * @param projectRes the {@link ProjectResources} for the project.
- * @param project the project.
- * @param credential the sandbox credential
- */
- public ProjectCallback(LayoutLibrary layoutLib,
- ProjectResources projectRes, IProject project, Object credential,
- GraphicalEditorPart editor) {
- mLayoutLib = layoutLib;
- mParentClassLoader = layoutLib.getClassLoader();
- mProjectRes = projectRes;
- mProject = project;
- mCredential = credential;
- mEditor = editor;
- }
-
- public Set<String> getMissingClasses() {
- return mMissingClasses;
- }
-
- public Set<String> getUninstantiatableClasses() {
- return mBrokenClasses;
- }
-
- /**
- * Sets the {@link LayoutLog} logger to use for error messages during problems
- *
- * @param logger the new logger to use, or null to clear it out
- */
- public void setLogger(LayoutLog logger) {
- mLogger = logger;
- }
-
- /**
- * Returns the {@link LayoutLog} logger used for error messages, or null
- *
- * @return the logger being used, or null if no logger is in use
- */
- public LayoutLog getLogger() {
- return mLogger;
- }
-
- /**
- * {@inheritDoc}
- *
- * This implementation goes through the output directory of the Eclipse project and loads the
- * <code>.class</code> file directly.
- */
- @Override
- @SuppressWarnings("unchecked")
- public Object loadView(String className, Class[] constructorSignature,
- Object[] constructorParameters)
- throws Exception {
- mUsed = true;
-
- if (className == null) {
- // Just make a plain <View> if you specify <view> without a class= attribute.
- className = CLASS_VIEW;
- }
-
- // look for a cached version
- Class<?> clazz = mLoadedClasses.get(className);
- if (clazz != null) {
- return instantiateClass(clazz, constructorSignature, constructorParameters);
- }
-
- // load the class.
-
- try {
- if (mLoader == null) {
- // Allow creating class loaders during rendering; may be prevented by the
- // RenderSecurityManager
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- mLoader = new ProjectClassLoader(mParentClassLoader, mProject);
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
- clazz = mLoader.loadClass(className);
- } catch (Exception e) {
- // Add the missing class to the list so that the renderer can print them later.
- // no need to log this.
- if (!className.equals(VIEW_FRAGMENT) && !className.equals(VIEW_INCLUDE)) {
- mMissingClasses.add(className);
- }
- }
-
- try {
- if (clazz != null) {
- // first try to instantiate it because adding it the list of loaded class so that
- // we don't add broken classes.
- Object view = instantiateClass(clazz, constructorSignature, constructorParameters);
- mLoadedClasses.put(className, clazz);
-
- return view;
- }
- } catch (Throwable e) {
- // Find root cause to log it.
- while (e.getCause() != null) {
- e = e.getCause();
- }
-
- appendToIdeLog(e, "%1$s failed to instantiate.", className); //$NON-NLS-1$
-
- // Add the missing class to the list so that the renderer can print them later.
- if (mLogger instanceof RenderLogger) {
- RenderLogger renderLogger = (RenderLogger) mLogger;
- renderLogger.recordThrowable(e);
-
- }
- mBrokenClasses.add(className);
- }
-
- // Create a mock view instead. We don't cache it in the mLoadedClasses map.
- // If any exception is thrown, we'll return a CFN with the original class name instead.
- try {
- clazz = mLoader.loadClass(SdkConstants.CLASS_MOCK_VIEW);
- Object view = instantiateClass(clazz, constructorSignature, constructorParameters);
-
- // Set the text of the mock view to the simplified name of the custom class
- Method m = view.getClass().getMethod("setText",
- new Class<?>[] { CharSequence.class });
- String label = getShortClassName(className);
- if (label.equals(VIEW_FRAGMENT)) {
- label = "<fragment>\n"
- + "Pick preview layout from the \"Fragment Layout\" context menu";
- } else if (label.equals(VIEW_INCLUDE)) {
- label = "Text";
- }
-
- m.invoke(view, label);
-
- // Call MockView.setGravity(Gravity.CENTER) to get the text centered in
- // MockViews.
- // TODO: Do this in layoutlib's MockView class instead.
- try {
- // Look up android.view.Gravity#CENTER - or can we just hard-code
- // the value (17) here?
- Class<?> gravity =
- Class.forName("android.view.Gravity", //$NON-NLS-1$
- true, view.getClass().getClassLoader());
- Field centerField = gravity.getField("CENTER"); //$NON-NLS-1$
- int center = centerField.getInt(null);
- m = view.getClass().getMethod("setGravity",
- new Class<?>[] { Integer.TYPE });
- // Center
- //int center = (0x0001 << 4) | (0x0001 << 0);
- m.invoke(view, Integer.valueOf(center));
- } catch (Exception e) {
- // Not important to center views
- }
-
- return view;
- } catch (Exception e) {
- // We failed to create and return a mock view.
- // Just throw back a CNF with the original class name.
- throw new ClassNotFoundException(className, e);
- }
- }
-
- private String getShortClassName(String fqcn) {
- // The name is typically a fully-qualified class name. Let's make it a tad shorter.
-
- if (fqcn.startsWith("android.")) { //$NON-NLS-1$
- // For android classes, convert android.foo.Name to android...Name
- int first = fqcn.indexOf('.');
- int last = fqcn.lastIndexOf('.');
- if (last > first) {
- return fqcn.substring(0, first) + ".." + fqcn.substring(last); //$NON-NLS-1$
- }
- } else {
- // For custom non-android classes, it's best to keep the 2 first segments of
- // the namespace, e.g. we want to get something like com.example...MyClass
- int first = fqcn.indexOf('.');
- first = fqcn.indexOf('.', first + 1);
- int last = fqcn.lastIndexOf('.');
- if (last > first) {
- return fqcn.substring(0, first) + ".." + fqcn.substring(last); //$NON-NLS-1$
- }
- }
-
- return fqcn;
- }
-
- /**
- * Returns the namespace for the project. The namespace contains a standard part + the
- * application package.
- *
- * @return The package namespace of the project or null in case of error.
- */
- @Override
- public String getNamespace() {
- if (mNamespace == null) {
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- ManifestData manifestData = AndroidManifestHelper.parseForData(mProject);
- if (manifestData != null) {
- String javaPackage = manifestData.getPackage();
- mNamespace = String.format(AdtConstants.NS_CUSTOM_RESOURCES, javaPackage);
- }
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
-
- return mNamespace;
- }
-
- @Override
- public Pair<ResourceType, String> resolveResourceId(int id) {
- if (mProjectRes != null) {
- return mProjectRes.resolveResourceId(id);
- }
-
- return null;
- }
-
- @Override
- public String resolveResourceId(int[] id) {
- if (mProjectRes != null) {
- return mProjectRes.resolveStyleable(id);
- }
-
- return null;
- }
-
- @Override
- public Integer getResourceId(ResourceType type, String name) {
- if (mProjectRes != null) {
- return mProjectRes.getResourceId(type, name);
- }
-
- return null;
- }
-
- /**
- * Returns whether the loader has received requests to load custom views. Note that
- * the custom view loading may not actually have succeeded; this flag only records
- * whether it was <b>requested</b>.
- * <p/>
- * This allows to efficiently only recreate when needed upon code change in the
- * project.
- *
- * @return true if the loader has been asked to load custom views
- */
- public boolean isUsed() {
- return mUsed;
- }
-
- /**
- * Instantiate a class object, using a specific constructor and parameters.
- * @param clazz the class to instantiate
- * @param constructorSignature the signature of the constructor to use
- * @param constructorParameters the parameters to use in the constructor.
- * @return A new class object, created using a specific constructor and parameters.
- * @throws Exception
- */
- @SuppressWarnings("unchecked")
- private Object instantiateClass(Class<?> clazz,
- Class[] constructorSignature,
- Object[] constructorParameters) throws Exception {
- Constructor<?> constructor = null;
-
- try {
- constructor = clazz.getConstructor(constructorSignature);
-
- } catch (NoSuchMethodException e) {
- // Custom views can either implement a 3-parameter, 2-parameter or a
- // 1-parameter. Let's synthetically build and try all the alternatives.
- // That's kind of like switching to the other box.
- //
- // The 3-parameter constructor takes the following arguments:
- // ...(Context context, AttributeSet attrs, int defStyle)
-
- int n = constructorSignature.length;
- if (n == 0) {
- // There is no parameter-less constructor. Nobody should ask for one.
- throw e;
- }
-
- for (int i = 3; i >= 1; i--) {
- if (i == n) {
- // Let's skip the one we know already fails
- continue;
- }
- Class[] sig = new Class[i];
- Object[] params = new Object[i];
-
- int k = i;
- if (n < k) {
- k = n;
- }
- System.arraycopy(constructorSignature, 0, sig, 0, k);
- System.arraycopy(constructorParameters, 0, params, 0, k);
-
- for (k++; k <= i; k++) {
- if (k == 2) {
- // Parameter 2 is the AttributeSet
- sig[k-1] = clazz.getClassLoader().loadClass("android.util.AttributeSet");
- params[k-1] = null;
-
- } else if (k == 3) {
- // Parameter 3 is the int defstyle
- sig[k-1] = int.class;
- params[k-1] = 0;
- }
- }
-
- constructorSignature = sig;
- constructorParameters = params;
-
- try {
- // Try again...
- constructor = clazz.getConstructor(constructorSignature);
- if (constructor != null) {
- // Found a suitable constructor, now let's use it.
- // (But let's warn the user if the simple View constructor was found
- // since Unexpected Things may happen if the attribute set constructors
- // are not found)
- if (constructorSignature.length < 2 && mLogger != null) {
- mLogger.warning("wrongconstructor", //$NON-NLS-1$
- String.format("Custom view %1$s is not using the 2- or 3-argument "
- + "View constructors; XML attributes will not work",
- clazz.getSimpleName()), null /*data*/);
- }
- break;
- }
- } catch (NoSuchMethodException e1) {
- // pass
- }
- }
-
- // If all the alternatives failed, throw the initial exception.
- if (constructor == null) {
- throw e;
- }
- }
-
- constructor.setAccessible(true);
- return constructor.newInstance(constructorParameters);
- }
-
- public void setLayoutParser(String layoutName, ILayoutPullParser layoutParser) {
- mLayoutName = layoutName;
- mLayoutEmbeddedParser = layoutParser;
- }
-
- @Override
- public ILayoutPullParser getParser(String layoutName) {
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- // Try to compute the ResourceValue for this layout since layoutlib
- // must be an older version which doesn't pass the value:
- if (mResourceResolver != null) {
- ResourceValue value = mResourceResolver.getProjectResource(ResourceType.LAYOUT,
- layoutName);
- if (value != null) {
- return getParser(value);
- }
- }
-
- return getParser(layoutName, null);
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
-
- @Override
- public ILayoutPullParser getParser(ResourceValue layoutResource) {
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- return getParser(layoutResource.getName(),
- new File(layoutResource.getValue()));
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
-
- private ILayoutPullParser getParser(String layoutName, File xml) {
- if (layoutName.equals(mLayoutName)) {
- ILayoutPullParser parser = mLayoutEmbeddedParser;
- // The parser should only be used once!! If it is included more than once,
- // subsequent includes should just use a plain pull parser that is not tied
- // to the XML model
- mLayoutEmbeddedParser = null;
- return parser;
- }
-
- // For included layouts, create a ContextPullParser such that we get the
- // layout editor behavior in included layouts as well - which for example
- // replaces <fragment> tags with <include>.
- if (xml != null && xml.isFile()) {
- ContextPullParser parser = new ContextPullParser(this, xml);
- try {
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- String xmlText = Files.toString(xml, Charsets.UTF_8);
- parser.setInput(new StringReader(xmlText));
- return parser;
- } catch (XmlPullParserException e) {
- appendToIdeLog(e, null);
- } catch (FileNotFoundException e) {
- // Shouldn't happen since we check isFile() above
- } catch (IOException e) {
- appendToIdeLog(e, null);
- }
- }
-
- return null;
- }
-
- @Override
- public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
- ResourceReference itemRef,
- int fullPosition, int typePosition, int fullChildPosition, int typeChildPosition,
- ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) {
-
- // Special case for the palette preview
- if (viewAttribute == ViewAttribute.TEXT
- && adapterView.getName().startsWith("android_widget_")) { //$NON-NLS-1$
- String name = adapterView.getName();
- if (viewRef.getName().equals("text2")) { //$NON-NLS-1$
- return "Sub Item";
- }
- if (fullPosition == 0) {
- String viewName = name.substring("android_widget_".length());
- if (viewName.equals(EXPANDABLE_LIST_VIEW)) {
- return "ExpandableList"; // ExpandableListView is too wide, character-wraps
- }
- return viewName;
- } else {
- return "Next Item";
- }
- }
-
- if (itemRef.isFramework()) {
- // Special case for list_view_item_2 and friends
- if (viewRef.getName().equals("text2")) { //$NON-NLS-1$
- return "Sub Item " + (fullPosition + 1);
- }
- }
-
- if (viewAttribute == ViewAttribute.TEXT && ((String) defaultValue).length() == 0) {
- return "Item " + (fullPosition + 1);
- }
-
- return null;
- }
-
- /**
- * For the given class, finds and returns the nearest super class which is a ListView
- * or an ExpandableListView or a GridView (which uses a list adapter), or returns null.
- *
- * @param clz the class of the view object
- * @return the fully qualified class name of the list ancestor, or null if there
- * is no list view ancestor
- */
- public static String getListAdapterViewFqcn(Class<?> clz) {
- String fqcn = clz.getName();
- if (fqcn.endsWith(LIST_VIEW)) { // including EXPANDABLE_LIST_VIEW
- return fqcn;
- } else if (fqcn.equals(FQCN_GRID_VIEW)) {
- return fqcn;
- } else if (fqcn.equals(FQCN_SPINNER)) {
- return fqcn;
- } else if (fqcn.startsWith(ANDROID_PKG_PREFIX)) {
- return null;
- }
- Class<?> superClass = clz.getSuperclass();
- if (superClass != null) {
- return getListAdapterViewFqcn(superClass);
- } else {
- // Should not happen; we would have encountered android.view.View first,
- // and it should have been covered by the ANDROID_PKG_PREFIX case above.
- return null;
- }
- }
-
- /**
- * Looks at the parent-chain of the view and if it finds a custom view, or a
- * CalendarView, within the given distance then it returns true. A ListView within a
- * CalendarView should not be assigned a custom list view type because it sets its own
- * and then attempts to cast the layout to its own type which would fail if the normal
- * default list item binding is used.
- */
- private boolean isWithinIllegalParent(Object viewObject, int depth) {
- String fqcn = viewObject.getClass().getName();
- if (fqcn.endsWith(CALENDAR_VIEW) || !fqcn.startsWith(ANDROID_PKG_PREFIX)) {
- return true;
- }
-
- if (depth > 0) {
- Result result = mLayoutLib.getViewParent(viewObject);
- if (result.isSuccess()) {
- Object parent = result.getData();
- if (parent != null) {
- return isWithinIllegalParent(parent, depth -1);
- }
- }
- }
-
- return false;
- }
-
- @Override
- public AdapterBinding getAdapterBinding(final ResourceReference adapterView,
- final Object adapterCookie, final Object viewObject) {
- // Look for user-recorded preference for layout to be used for previews
- if (adapterCookie instanceof UiViewElementNode) {
- UiViewElementNode uiNode = (UiViewElementNode) adapterCookie;
- AdapterBinding binding = LayoutMetadata.getNodeBinding(viewObject, uiNode);
- if (binding != null) {
- return binding;
- }
- } else if (adapterCookie instanceof Map<?,?>) {
- @SuppressWarnings("unchecked")
- Map<String, String> map = (Map<String, String>) adapterCookie;
- AdapterBinding binding = LayoutMetadata.getNodeBinding(viewObject, map);
- if (binding != null) {
- return binding;
- }
- }
-
- if (viewObject == null) {
- return null;
- }
-
- // Is this a ListView or ExpandableListView? If so, return its fully qualified
- // class name, otherwise return null. This is used to filter out other types
- // of AdapterViews (such as Spinners) where we don't want to use the list item
- // binding.
- String listFqcn = getListAdapterViewFqcn(viewObject.getClass());
- if (listFqcn == null) {
- return null;
- }
-
- // Is this ListView nested within an "illegal" container, such as a CalendarView?
- // If so, don't change the bindings below. Some views, such as CalendarView, and
- // potentially some custom views, might be doing specific things with the ListView
- // that could break if we add our own list binding, so for these leave the list
- // alone.
- if (isWithinIllegalParent(viewObject, 2)) {
- return null;
- }
-
- int count = listFqcn.endsWith(GRID_VIEW) ? 24 : 12;
- AdapterBinding binding = new AdapterBinding(count);
- if (listFqcn.endsWith(EXPANDABLE_LIST_VIEW)) {
- binding.addItem(new DataBindingItem(LayoutMetadata.DEFAULT_EXPANDABLE_LIST_ITEM,
- true /* isFramework */, 1));
- } else if (listFqcn.equals(SPINNER)) {
- binding.addItem(new DataBindingItem(LayoutMetadata.DEFAULT_SPINNER_ITEM,
- true /* isFramework */, 1));
- } else {
- binding.addItem(new DataBindingItem(LayoutMetadata.DEFAULT_LIST_ITEM,
- true /* isFramework */, 1));
- }
-
- return binding;
- }
-
- /**
- * Sets the {@link ResourceResolver} to be used when looking up resources
- *
- * @param resolver the resolver to use
- */
- public void setResourceResolver(ResourceResolver resolver) {
- mResourceResolver = resolver;
- }
-
- // Append the given message to the ADT log. Bypass the sandbox if necessary
- // such that we can write to the log file.
- private void appendToIdeLog(Throwable exception, String format, Object ... args) {
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- AdtPlugin.log(exception, format, args);
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
-
- @Override
- public ActionBarCallback getActionBarCallback() {
- return new ActionBarHandler(mEditor);
- }
-
- @Override
- public boolean supports(int feature) {
- return feature <= Features.LAST_CAPABILITY;
- }
-}