diff options
author | Tor Norbye <tnorbye@google.com> | 2012-05-25 15:16:23 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2012-05-25 16:31:58 -0700 |
commit | 64bb5c1f6139d05de62c8152f64aad8898df95b2 (patch) | |
tree | 5f3fb8323f4cf8d1293f469da93ab3c4f546abb0 | |
parent | 6474a9620babf53082fe6e3326e602b4c7b3052d (diff) | |
download | sdk-64bb5c1f6139d05de62c8152f64aad8898df95b2.zip sdk-64bb5c1f6139d05de62c8152f64aad8898df95b2.tar.gz sdk-64bb5c1f6139d05de62c8152f64aad8898df95b2.tar.bz2 |
Code completion in custom views now picks up newly added attrs
When ADT encounters a custom view, it creates a view descriptor for
that custom view, and in ADT 20 it also finds the associated styles
and records these as attributes, which means code completion and the
property sheet will show these properties.
However, once the descriptor has been lazily created, it never
changes. If you go and edit the styles.xml file specifying the
attributes for your custom view, you need to restart the IDE before
code completion will pick up these changes.
This changeset makes this behavior more dynamic: It will now pick up
edits to the files specifying styles for a custom view.
Change-Id: I365119e18c74378a0a039c1e7a22641c94acf002
4 files changed, 111 insertions, 10 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt index cbf3d10..5ac89f5 100644 --- a/eclipse/dictionary.txt +++ b/eclipse/dictionary.txt @@ -293,6 +293,7 @@ thematically themed thumbnail timestamp +timestamps tmp toolbar tooltip diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java index 613a68f..7cfe791 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java @@ -469,4 +469,17 @@ public class ElementDescriptor implements Comparable<ElementDescriptor> { public int compareTo(ElementDescriptor o) { return mUiName.compareToIgnoreCase(o.mUiName); } + + /** + * Ensures that this view descriptor's attribute list is up to date. This is + * always the case for all the builtin descriptors, but for example for a + * custom view, it could be changing dynamically so caches may have to be + * recomputed. This method will return true if nothing changed, and false if + * it recomputed its info. + * + * @return true if the attributes are already up to date and nothing changed + */ + public boolean syncAttributes() { + return true; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java index a110c65..ab4e1e9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java @@ -16,11 +16,11 @@ package com.android.ide.eclipse.adt.internal.editors.layout.descriptors; -import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.sdklib.SdkConstants.CLASS_VIEWGROUP; import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI; import static com.android.tools.lint.detector.api.LintConstants.URI_PREFIX; import static com.android.util.XmlUtils.ANDROID_NS_NAME_PREFIX; +import static com.android.util.XmlUtils.ANDROID_URI; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -44,12 +44,14 @@ import com.android.ide.eclipse.adt.internal.sdk.ProjectState; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; +import com.google.common.collect.Maps; import com.google.common.collect.ObjectArrays; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.IClassFile; @@ -193,7 +195,8 @@ public final class CustomViewDescriptorService { // we have a valid parent, lets create a new ViewElementDescriptor. List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>(); List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>(); - findCustomDescriptors(project, type, attrList, paramList); + Map<ResourceFile, Long> files = findCustomDescriptors(project, type, + attrList, paramList); AttributeDescriptor[] attributes = getAttributeDescriptor(type, parentDescriptor); @@ -209,7 +212,8 @@ public final class CustomViewDescriptorService { ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn, attributes, layoutAttributes, - parentDescriptor.getChildren()); + parentDescriptor.getChildren(), + project, files); descriptor.setSuperClass(parentDescriptor); synchronized (mCustomDescriptorMap) { @@ -270,7 +274,7 @@ public final class CustomViewDescriptorService { } /** Compute/find the styleable resources for the given type, if possible */ - private void findCustomDescriptors( + private Map<ResourceFile, Long> findCustomDescriptors( IProject project, IType type, List<AttributeDescriptor> customAttributes, @@ -286,6 +290,8 @@ public final class CustomViewDescriptorService { Set<ResourceFile> resourceFiles = findAttrsFiles(library, className); if (resourceFiles != null && resourceFiles.size() > 0) { String appUri = getAppResUri(project); + Map<ResourceFile, Long> timestamps = + Maps.newHashMapWithExpectedSize(resourceFiles.size()); for (ResourceFile file : resourceFiles) { AttrsXmlParser attrsXmlParser = getParser(file); String fqcn = type.getFullyQualifiedName(); @@ -300,8 +306,14 @@ public final class CustomViewDescriptorService { classInfo, "Layout", null /*superClassInfo*/); //$NON-NLS-1$ attrsXmlParser.loadLayoutParamsAttributes(layoutInfo); appendAttributes(customLayoutAttributes, layoutInfo.getAttributes(), appUri); + + timestamps.put(file, file.getFile().getModificationStamp()); } + + return timestamps; } + + return null; } /** @@ -309,7 +321,7 @@ public final class CustomViewDescriptorService { * attributes for the given class name */ @Nullable - private Set<ResourceFile> findAttrsFiles(IProject library, String className) { + private static Set<ResourceFile> findAttrsFiles(IProject library, String className) { Set<ResourceFile> resourceFiles = null; ResourceManager manager = ResourceManager.getInstance(); ProjectResources resources = manager.getProjectResources(library); @@ -338,7 +350,7 @@ public final class CustomViewDescriptorService { * attrs.xml file in the same project. */ @Nullable - private IProject getProjectDeclaringType(IType type) { + private static IProject getProjectDeclaringType(IType type) { IClassFile classFile = type.getClassFile(); if (classFile != null) { IPath path = classFile.getPath(); @@ -358,7 +370,7 @@ public final class CustomViewDescriptorService { } /** Returns the name space to use for application attributes */ - private String getAppResUri(IProject project) { + private static String getAppResUri(IProject project) { String appResource; ProjectState projectState = Sdk.getProjectState(project); if (projectState != null && projectState.isLibrary()) { @@ -459,7 +471,7 @@ public final class CustomViewDescriptorService { ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn, getAttributeDescriptor(type, parentDescriptor), getLayoutAttributeDescriptors(type, parentDescriptor), - children); + children, project, null); descriptor.setSuperClass(parentDescriptor); // add it to the map @@ -504,10 +516,14 @@ public final class CustomViewDescriptorService { return parentDescriptor.getLayoutAttributes(); } - private static class CustomViewDescriptor extends ViewElementDescriptor { + private class CustomViewDescriptor extends ViewElementDescriptor { + private Map<ResourceFile, Long> mTimeStamps; + private IProject mProject; + public CustomViewDescriptor(String name, String fqcn, AttributeDescriptor[] attributes, AttributeDescriptor[] layoutAttributes, - ElementDescriptor[] children) { + ElementDescriptor[] children, IProject project, + Map<ResourceFile, Long> timestamps) { super( fqcn, // xml name name, // ui name @@ -519,6 +535,8 @@ public final class CustomViewDescriptorService { children, false // mandatory ); + mTimeStamps = timestamps; + mProject = project; } @Override @@ -533,5 +551,71 @@ public final class CustomViewDescriptorService { return iconFactory.getIcon("customView"); //$NON-NLS-1$ } + + @Override + public boolean syncAttributes() { + // Check if any of the descriptors + if (mTimeStamps != null) { + // Prevent checking actual file timestamps too frequently on rapid burst calls + long now = System.currentTimeMillis(); + if (now - sLastCheck < 1000) { + return true; + } + sLastCheck = now; + + // Check whether the resource files (typically just one) which defined + // custom attributes for this custom view have changed, and if so, + // refresh the attribute descriptors. + // This doesn't work the cases where you add descriptors for a custom + // view after using it, or add attributes in a separate file, but those + // scenarios aren't quite as common (and would require a bit more expensive + // analysis.) + for (Map.Entry<ResourceFile, Long> entry : mTimeStamps.entrySet()) { + ResourceFile file = entry.getKey(); + Long timestamp = entry.getValue(); + boolean recompute = false; + if (file.getFile().getModificationStamp() > timestamp.longValue()) { + // One or more attributes changed: recompute + recompute = true; + mParserCache.remove(file); + } + + if (recompute) { + IJavaProject javaProject = JavaCore.create(mProject); + String fqcn = getFullClassName(); + IType type = null; + try { + type = javaProject.findType(fqcn); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + if (type == null || !type.exists()) { + return true; + } + + List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>(); + List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>(); + + mTimeStamps = findCustomDescriptors(mProject, type, attrList, paramList); + + ViewElementDescriptor parentDescriptor = getSuperClassDesc(); + AttributeDescriptor[] attributes = + getAttributeDescriptor(type, parentDescriptor); + if (!attrList.isEmpty()) { + attributes = join(attrList, attributes); + } + attributes = attrList.toArray(new AttributeDescriptor[attrList.size()]); + setAttributes(attributes); + + return false; + } + } + } + + return true; + } } + + /** Timestamp of the most recent {@link CustomViewDescriptor#syncAttributes} check */ + private static long sLastCheck; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java index ec19ea7..fef6022 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java @@ -66,6 +66,9 @@ public class UiViewElementNode extends UiElementNode { */ @Override public AttributeDescriptor[] getAttributeDescriptors() { + if (!getDescriptor().syncAttributes()) { + mCachedAttributeDescriptors = null; + } if (mCachedAttributeDescriptors != null) { return mCachedAttributeDescriptors; } |