aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationAttributesPart.java175
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationPage.java136
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationToggle.java312
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/InstrumentationPage.java102
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewExportPart.java123
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewInfoPart.java87
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java124
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewPage.java165
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/PermissionPage.java111
9 files changed, 1335 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationAttributesPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationAttributesPart.java
new file mode 100644
index 000000000..7d3f6a89f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationAttributesPart.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.ui.UiElementPart;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+/**
+ * Application's attributes section part for Application page.
+ * <p/>
+ * This part is displayed at the top of the application page and displays all the possible
+ * attributes of an application node in the AndroidManifest (icon, class name, label, etc.)
+ */
+final class ApplicationAttributesPart extends UiElementPart {
+
+ /** Listen to changes to the UI node for <application> and updates the UI */
+ private AppNodeUpdateListener mAppNodeUpdateListener;
+ /** ManagedForm needed to create the UI controls */
+ private IManagedForm mManagedForm;
+
+ public ApplicationAttributesPart(Composite body, FormToolkit toolkit, ManifestEditor editor,
+ UiElementNode applicationUiNode) {
+ super(body, toolkit, editor, applicationUiNode,
+ "Application Attributes", // section title
+ "Defines the attributes specific to the application.", // section description
+ Section.TWISTIE | Section.EXPANDED);
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handle by the this part.
+ */
+ @Override
+ public void setUiElementNode(UiElementNode uiElementNode) {
+ super.setUiElementNode(uiElementNode);
+
+ createUiAttributes(mManagedForm);
+ }
+
+ /* (non-java doc)
+ * Create the controls to edit the attributes for the given ElementDescriptor.
+ * <p/>
+ * This MUST not be called by the constructor. Instead it must be called from
+ * <code>initialize</code> (i.e. right after the form part is added to the managed form.)
+ * <p/>
+ * Derived classes can override this if necessary.
+ *
+ * @param managedForm The owner managed form
+ */
+ @Override
+ protected void createFormControls(final IManagedForm managedForm) {
+ mManagedForm = managedForm;
+ setTable(createTableLayout(managedForm.getToolkit(), 4 /* numColumns */));
+
+ mAppNodeUpdateListener = new AppNodeUpdateListener();
+ getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
+
+ createUiAttributes(mManagedForm);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
+ getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
+ mAppNodeUpdateListener = null;
+ }
+ }
+
+ @Override
+ protected void createUiAttributes(IManagedForm managedForm) {
+ Composite table = getTable();
+ if (table == null || managedForm == null) {
+ return;
+ }
+
+ // Remove any old UI controls
+ for (Control c : table.getChildren()) {
+ c.dispose();
+ }
+
+ UiElementNode uiElementNode = getUiElementNode();
+ AttributeDescriptor[] attr_desc_list = uiElementNode.getAttributeDescriptors();
+
+ // Display the attributes in 2 columns:
+ // attr 0 | attr 4
+ // attr 1 | attr 5
+ // attr 2 | attr 6
+ // attr 3 | attr 7
+ // that is we have to fill the grid in order 0, 4, 1, 5, 2, 6, 3, 7
+ // thus index = i/2 + (i is odd * n/2)
+ int n = attr_desc_list.length;
+ int n2 = (int) Math.ceil(n / 2.0);
+ for (int i = 0; i < n; i++) {
+ AttributeDescriptor attr_desc = attr_desc_list[i / 2 + (i & 1) * n2];
+ if (attr_desc instanceof XmlnsAttributeDescriptor) {
+ // Do not show hidden attributes
+ continue;
+ }
+
+ UiAttributeNode ui_attr = uiElementNode.findUiAttribute(attr_desc);
+ if (ui_attr != null) {
+ ui_attr.createUiControl(table, managedForm);
+ } else {
+ // The XML has an extra attribute which wasn't declared in
+ // AndroidManifestDescriptors. This is not a problem, we just ignore it.
+ AdtPlugin.log(IStatus.WARNING,
+ "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$
+ attr_desc.getXmlLocalName(),
+ uiElementNode.getDescriptor().getXmlName());
+ }
+ }
+
+ if (n == 0) {
+ createLabel(table, managedForm.getToolkit(),
+ "No attributes to display, waiting for SDK to finish loading...",
+ null /* tooltip */ );
+ }
+
+ // Initialize the enabled/disabled state
+ if (mAppNodeUpdateListener != null) {
+ mAppNodeUpdateListener.uiElementNodeUpdated(uiElementNode, null /* state, not used */);
+ }
+
+ // Tell the section that the layout has changed.
+ layoutChanged();
+ }
+
+ /**
+ * This listener synchronizes the UI with the actual presence of the application XML node.
+ */
+ private class AppNodeUpdateListener implements IUiUpdateListener {
+ @Override
+ public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
+ // The UiElementNode for the application XML node always exists, even
+ // if there is no corresponding XML node in the XML file.
+ //
+ // We enable the UI here if the XML node is not null.
+ Composite table = getTable();
+ boolean exists = (ui_node.getXmlNode() != null);
+ if (table != null && table.getEnabled() != exists) {
+ table.setEnabled(exists);
+ for (Control c : table.getChildren()) {
+ c.setEnabled(exists);
+ }
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationPage.java
new file mode 100644
index 000000000..06a3d3f3e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationPage.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+
+/**
+ * Page for "Application" settings, part of the AndroidManifest form editor.
+ * <p/>
+ * Useful reference:
+ * <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
+ * http://www.eclipse.org/articles/Article-Forms/article.html</a>
+ */
+public final class ApplicationPage extends FormPage implements IPageImageProvider {
+ /** Page id used for switching tabs programmatically */
+ public final static String PAGE_ID = "application_page"; //$NON-NLS-1$
+
+ /** Container editor */
+ ManifestEditor mEditor;
+ /** The Application Toogle part */
+ private ApplicationToggle mTooglePart;
+ /** The Application Attributes part */
+ private ApplicationAttributesPart mAttrPart;
+ /** The tree view block */
+ private UiTreeBlock mTreeBlock;
+
+ public ApplicationPage(ManifestEditor editor) {
+ super(editor, PAGE_ID, "Application"); // tab's label, keep it short
+ mEditor = editor;
+ }
+
+ @Override
+ public Image getPageImage() {
+ return IconFactory.getInstance().getIcon(getTitle(),
+ IconFactory.COLOR_BLUE,
+ IconFactory.SHAPE_RECT);
+ }
+
+ /**
+ * Creates the content in the form hosted in this page.
+ *
+ * @param managedForm the form hosted in this page.
+ */
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ super.createFormContent(managedForm);
+ ScrolledForm form = managedForm.getForm();
+ form.setText("Android Manifest Application");
+ form.setImage(AdtPlugin.getAndroidLogo());
+
+ UiElementNode appUiNode = getUiApplicationNode();
+
+ Composite body = form.getBody();
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ // We usually prefer to have a ColumnLayout here. However
+ // MasterDetailsBlock.createContent() below will reset the body's layout to a grid layout.
+ mTooglePart = new ApplicationToggle(body, toolkit, mEditor, appUiNode);
+ mTooglePart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ managedForm.addPart(mTooglePart);
+ mAttrPart = new ApplicationAttributesPart(body, toolkit, mEditor, appUiNode);
+ mAttrPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ managedForm.addPart(mAttrPart);
+
+ mTreeBlock = new UiTreeBlock(mEditor, appUiNode,
+ false /* autoCreateRoot */,
+ null /* element filters */,
+ "Application Nodes",
+ "List of all elements in the application");
+ mTreeBlock.createContent(managedForm);
+ }
+
+ /**
+ * Retrieves the application UI node. Since this is a mandatory node, it *always*
+ * exists, even if there is no matching XML node.
+ */
+ private UiElementNode getUiApplicationNode() {
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+ if (manifestDescriptor != null) {
+ ElementDescriptor desc = manifestDescriptor.getApplicationElement();
+ return mEditor.getUiRootNode().findUiChildNode(desc.getXmlName());
+ } else {
+ // return the ui root node, as a dummy application root node.
+ return mEditor.getUiRootNode();
+ }
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handled by the sub parts.
+ */
+ public void refreshUiApplicationNode() {
+ UiElementNode appUiNode = getUiApplicationNode();
+ if (mTooglePart != null) {
+ mTooglePart.setUiElementNode(appUiNode);
+ }
+ if (mAttrPart != null) {
+ mAttrPart.setUiElementNode(appUiNode);
+ }
+ if (mTreeBlock != null) {
+ mTreeBlock.changeRootAndDescriptors(appUiNode,
+ null /* element filters */,
+ true /* refresh */);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationToggle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationToggle.java
new file mode 100644
index 000000000..159f08959
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/ApplicationToggle.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.ui.UiElementPart;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener.UiUpdateState;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.utils.SdkUtils;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * Appllication Toogle section part for application page.
+ */
+final class ApplicationToggle extends UiElementPart {
+
+ /** Checkbox indicating whether an application node is present */
+ private Button mCheckbox;
+ /** Listen to changes to the UI node for <application> and updates the checkbox */
+ private AppNodeUpdateListener mAppNodeUpdateListener;
+ /** Internal flag to know where we're programmatically modifying the checkbox and we want to
+ * avoid triggering the checkbox's callback. */
+ public boolean mInternalModification;
+ private FormText mTooltipFormText;
+
+ public ApplicationToggle(Composite body, FormToolkit toolkit, ManifestEditor editor,
+ UiElementNode applicationUiNode) {
+ super(body, toolkit, editor, applicationUiNode,
+ "Application Toggle",
+ null, /* description */
+ Section.TWISTIE | Section.EXPANDED);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
+ getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
+ mAppNodeUpdateListener = null;
+ }
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handle by the this part.
+ */
+ @Override
+ public void setUiElementNode(UiElementNode uiElementNode) {
+ super.setUiElementNode(uiElementNode);
+
+ updateTooltip();
+
+ // Set the state of the checkbox
+ mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
+ UiUpdateState.CHILDREN_CHANGED);
+ }
+
+ /**
+ * Create the controls to edit the attributes for the given ElementDescriptor.
+ * <p/>
+ * This MUST not be called by the constructor. Instead it must be called from
+ * <code>initialize</code> (i.e. right after the form part is added to the managed form.)
+ *
+ * @param managedForm The owner managed form
+ */
+ @Override
+ protected void createFormControls(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+ Composite table = createTableLayout(toolkit, 1 /* numColumns */);
+
+ mTooltipFormText = createFormText(table, toolkit, true, "<form></form>",
+ false /* setupLayoutData */);
+ updateTooltip();
+
+ mCheckbox = toolkit.createButton(table,
+ "Define an <application> tag in the AndroidManifest.xml",
+ SWT.CHECK);
+ mCheckbox.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
+ mCheckbox.setSelection(false);
+ mCheckbox.addSelectionListener(new CheckboxSelectionListener());
+
+ mAppNodeUpdateListener = new AppNodeUpdateListener();
+ getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
+
+ // Initialize the state of the checkbox
+ mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
+ UiUpdateState.CHILDREN_CHANGED);
+
+ // Tell the section that the layout has changed.
+ layoutChanged();
+ }
+
+ /**
+ * Updates the application tooltip in the form text.
+ * If there is no tooltip, the form text is hidden.
+ */
+ private void updateTooltip() {
+ boolean isVisible = false;
+
+ String tooltip = getUiElementNode().getDescriptor().getTooltip();
+ if (tooltip != null) {
+ tooltip = DescriptorsUtils.formatFormText(tooltip,
+ getUiElementNode().getDescriptor(),
+ Sdk.getCurrent().getDocumentationBaseUrl());
+
+ mTooltipFormText.setText(tooltip, true /* parseTags */, true /* expandURLs */);
+ mTooltipFormText.setImage(DescriptorsUtils.IMAGE_KEY, AdtPlugin.getAndroidLogo());
+ mTooltipFormText.addHyperlinkListener(getEditor().createHyperlinkListener());
+ isVisible = true;
+ }
+
+ mTooltipFormText.setVisible(isVisible);
+ }
+
+ /**
+ * This listener synchronizes the XML application node when the checkbox
+ * is changed by the user.
+ */
+ private class CheckboxSelectionListener extends SelectionAdapter {
+ private Node mUndoXmlNode;
+ private Node mUndoXmlParent;
+ private Node mUndoXmlNextNode;
+ private Node mUndoXmlNextElement;
+ private Document mUndoXmlDocument;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ if (!mInternalModification && getUiElementNode() != null) {
+ getUiElementNode().getEditor().wrapUndoEditXmlModel(
+ mCheckbox.getSelection()
+ ? "Create or restore Application node"
+ : "Remove Application node",
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mCheckbox.getSelection()) {
+ // The user wants an <application> node.
+ // Either restore a previous one
+ // or create a full new one.
+ boolean create = true;
+ if (mUndoXmlNode != null) {
+ create = !restoreApplicationNode();
+ }
+ if (create) {
+ getUiElementNode().createXmlNode();
+ }
+ } else {
+ // Users no longer wants the <application> node.
+ removeApplicationNode();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Restore a previously "saved" application node.
+ *
+ * @return True if the node could be restored, false otherwise.
+ */
+ private boolean restoreApplicationNode() {
+ if (mUndoXmlDocument == null || mUndoXmlNode == null) {
+ return false;
+ }
+
+ // Validate node references...
+ mUndoXmlParent = validateNode(mUndoXmlDocument, mUndoXmlParent);
+ mUndoXmlNextNode = validateNode(mUndoXmlDocument, mUndoXmlNextNode);
+ mUndoXmlNextElement = validateNode(mUndoXmlDocument, mUndoXmlNextElement);
+
+ if (mUndoXmlParent == null){
+ // If the parent node doesn't exist, try to find a new manifest node.
+ // If it doesn't exist, create it.
+ mUndoXmlParent = getUiElementNode().getUiParent().prepareCommit();
+ mUndoXmlNextNode = null;
+ mUndoXmlNextElement = null;
+ }
+
+ boolean success = false;
+ if (mUndoXmlParent != null) {
+ // If the parent is still around, reuse the same node.
+
+ // Ideally we want to insert the node before what used to be its next sibling.
+ // If that's not possible, we try to insert it before its next sibling element.
+ // If that's not possible either, it will be inserted at the end of the parent's.
+ Node next = mUndoXmlNextNode;
+ if (next == null) {
+ next = mUndoXmlNextElement;
+ }
+ mUndoXmlParent.insertBefore(mUndoXmlNode, next);
+ if (next == null) {
+ Text sep = mUndoXmlDocument.createTextNode(SdkUtils.getLineSeparator());
+ mUndoXmlParent.insertBefore(sep, null); // insert separator before end tag
+ }
+ success = true;
+ }
+
+ // Remove internal references to avoid using them twice
+ mUndoXmlParent = null;
+ mUndoXmlNextNode = null;
+ mUndoXmlNextElement = null;
+ mUndoXmlNode = null;
+ mUndoXmlDocument = null;
+ return success;
+ }
+
+ /**
+ * Validates that the given xml_node is still either the root node or one of its
+ * direct descendants.
+ *
+ * @param root_node The root of the node hierarchy to examine.
+ * @param xml_node The XML node to find.
+ * @return Returns xml_node if it is, otherwise returns null.
+ */
+ private Node validateNode(Node root_node, Node xml_node) {
+ if (root_node == xml_node) {
+ return xml_node;
+ } else {
+ for (Node node = root_node.getFirstChild(); node != null;
+ node = node.getNextSibling()) {
+ if (root_node == xml_node || validateNode(node, xml_node) != null) {
+ return xml_node;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the <application> node from the hierarchy.
+ * Before doing that, we try to remember where it was so that we can put it back
+ * in the same place.
+ */
+ private void removeApplicationNode() {
+ // Make sure the node actually exists...
+ Node xml_node = getUiElementNode().getXmlNode();
+ if (xml_node == null) {
+ return;
+ }
+
+ // Save its parent, next sibling and next element
+ mUndoXmlDocument = xml_node.getOwnerDocument();
+ mUndoXmlParent = xml_node.getParentNode();
+ mUndoXmlNextNode = xml_node.getNextSibling();
+ mUndoXmlNextElement = mUndoXmlNextNode;
+ while (mUndoXmlNextElement != null &&
+ mUndoXmlNextElement.getNodeType() != Node.ELEMENT_NODE) {
+ mUndoXmlNextElement = mUndoXmlNextElement.getNextSibling();
+ }
+
+ // Actually remove the node from the hierarchy and keep it here.
+ // The returned node looses its parents/siblings pointers.
+ mUndoXmlNode = getUiElementNode().deleteXmlNode();
+ }
+ }
+
+ /**
+ * This listener synchronizes the UI (i.e. the checkbox) with the
+ * actual presence of the application XML node.
+ */
+ private class AppNodeUpdateListener implements IUiUpdateListener {
+ @Override
+ public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
+ // The UiElementNode for the application XML node always exists, even
+ // if there is no corresponding XML node in the XML file.
+ //
+ // To update the checkbox to reflect the actual state, we just need
+ // to check if the XML node is null.
+ try {
+ mInternalModification = true;
+ boolean exists = ui_node.getXmlNode() != null;
+ if (mCheckbox.getSelection() != exists) {
+ mCheckbox.setSelection(exists);
+ }
+ } finally {
+ mInternalModification = false;
+ }
+
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/InstrumentationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/InstrumentationPage.java
new file mode 100644
index 000000000..a8bb34691
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/InstrumentationPage.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * Page for instrumentation settings, part of the AndroidManifest form editor.
+ */
+public final class InstrumentationPage extends FormPage implements IPageImageProvider {
+ /** Page id used for switching tabs programmatically */
+ public final static String PAGE_ID = "instrumentation_page"; //$NON-NLS-1$
+
+ /** Container editor */
+ ManifestEditor mEditor;
+
+ private UiTreeBlock mTreeBlock;
+
+ public InstrumentationPage(ManifestEditor editor) {
+ super(editor, PAGE_ID, "Instrumentation"); // tab's label, keep it short
+ mEditor = editor;
+ }
+
+ @Override
+ public Image getPageImage() {
+ return IconFactory.getInstance().getIcon(getTitle(),
+ IconFactory.COLOR_GREEN,
+ IconFactory.SHAPE_RECT);
+ }
+
+ /**
+ * Creates the content in the form hosted in this page.
+ *
+ * @param managedForm the form hosted in this page.
+ */
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ super.createFormContent(managedForm);
+ ScrolledForm form = managedForm.getForm();
+ form.setText("Android Manifest Instrumentation");
+ form.setImage(AdtPlugin.getAndroidLogo());
+
+ UiElementNode manifest = mEditor.getUiRootNode();
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+
+ ElementDescriptor[] descriptorFilters = null;
+ if (manifestDescriptor != null) {
+ descriptorFilters = new ElementDescriptor[] {
+ manifestDescriptor.getInstrumentationElement(),
+ };
+ }
+
+ mTreeBlock = new UiTreeBlock(mEditor, manifest,
+ true /* autoCreateRoot */,
+ descriptorFilters,
+ "Instrumentation",
+ "List of instrumentations defined in the manifest");
+ mTreeBlock.createContent(managedForm);
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handled by the sub parts.
+ */
+ public void refreshUiNode() {
+ if (mTreeBlock != null) {
+ UiElementNode manifest = mEditor.getUiRootNode();
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+
+ mTreeBlock.changeRootAndDescriptors(manifest,
+ new ElementDescriptor[] {
+ manifestDescriptor.getInstrumentationElement()
+ },
+ true /* refresh */);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewExportPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewExportPart.java
new file mode 100644
index 000000000..b0eb75a2d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewExportPart.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart;
+import com.android.ide.eclipse.adt.internal.project.ExportHelper;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.part.FileEditorInput;
+
+/**
+ * Export section part for overview page.
+ */
+final class OverviewExportPart extends ManifestSectionPart {
+
+ private final OverviewPage mOverviewPage;
+
+ public OverviewExportPart(OverviewPage overviewPage, final Composite body, FormToolkit toolkit,
+ ManifestEditor editor) {
+ super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
+ mOverviewPage = overviewPage;
+ Section section = getSection();
+ section.setText("Exporting");
+
+ final IProject project = getProject();
+ boolean isLibrary = false;
+ if (project != null) {
+ ProjectState state = Sdk.getProjectState(project);
+ if (state != null) {
+ isLibrary = state.isLibrary();
+ }
+ }
+
+ if (isLibrary) {
+ section.setDescription("Library project cannot be exported.");
+ Composite table = createTableLayout(toolkit, 2 /* numColumns */);
+ createFormText(table, toolkit, true, "<form></form>", false /* setupLayoutData */);
+ } else {
+ section.setDescription("To export the application for distribution, you have the following options:");
+
+ Composite table = createTableLayout(toolkit, 2 /* numColumns */);
+
+ StringBuffer buf = new StringBuffer();
+ buf.append("<form><li><a href=\"wizard\">"); //$NON-NLS-1$
+ buf.append("Use the Export Wizard");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(" to export and sign an APK");
+ buf.append("</li>"); //$NON-NLS-1$
+ buf.append("<li><a href=\"manual\">"); //$NON-NLS-1$
+ buf.append("Export an unsigned APK");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(" and sign it manually");
+ buf.append("</li></form>"); //$NON-NLS-1$
+
+ FormText text = createFormText(table, toolkit, true, buf.toString(),
+ false /* setupLayoutData */);
+ text.addHyperlinkListener(new HyperlinkAdapter() {
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ if (project != null) {
+ if ("manual".equals(e.data)) { //$NON-NLS-1$
+ // now we can export an unsigned apk for the project.
+ ExportHelper.exportUnsignedReleaseApk(project);
+ } else {
+ // call the export wizard
+ StructuredSelection selection = new StructuredSelection(project);
+
+ ExportWizard wizard = new ExportWizard();
+ wizard.init(PlatformUI.getWorkbench(), selection);
+ WizardDialog dialog = new WizardDialog(body.getShell(), wizard);
+ dialog.open();
+ }
+ }
+ }
+ });
+ }
+
+ layoutChanged();
+ }
+
+ /**
+ * Returns the project of the edited file.
+ */
+ private IProject getProject() {
+ IEditorInput input = mOverviewPage.mEditor.getEditorInput();
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput)input;
+ IFile file = fileInput.getFile();
+ return file.getProject();
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewInfoPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewInfoPart.java
new file mode 100644
index 000000000..98f2f9cc2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewInfoPart.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.UiElementPart;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+/**
+ * Generic info section part for overview page: it displays all the attributes from
+ * the manifest element.
+ */
+final class OverviewInfoPart extends UiElementPart {
+
+ private IManagedForm mManagedForm;
+
+ public OverviewInfoPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
+ super(body, toolkit, editor,
+ getManifestUiNode(editor), // uiElementNode
+ "Manifest General Attributes", // section title
+ "Defines general information about the AndroidManifest.xml", // section description
+ Section.TWISTIE | Section.EXPANDED);
+ }
+
+ /**
+ * Retrieves the UiElementNode that this part will edit. The node must exist
+ * and can't be null, by design, because it's a mandatory node.
+ */
+ private static UiElementNode getManifestUiNode(ManifestEditor editor) {
+ AndroidManifestDescriptors manifestDescriptors = editor.getManifestDescriptors();
+ if (manifestDescriptors != null) {
+ ElementDescriptor desc = manifestDescriptors.getManifestElement();
+ if (editor.getUiRootNode().getDescriptor() == desc) {
+ return editor.getUiRootNode();
+ } else {
+ return editor.getUiRootNode().findUiChildNode(desc.getXmlName());
+ }
+ }
+
+ // No manifest descriptor: we have a dummy UiRootNode, so we return that.
+ // The editor will be reloaded once we have the proper descriptors anyway.
+ return editor.getUiRootNode();
+ }
+
+ /**
+ * Overridden in order to capture the current managed form.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected void createFormControls(final IManagedForm managedForm) {
+ mManagedForm = managedForm;
+ super.createFormControls(managedForm);
+ }
+
+ /**
+ * Removes any existing Attribute UI widgets and recreate them if the SDK has changed.
+ * <p/>
+ * This is called by {@link OverviewPage#refreshUiApplicationNode()} when the
+ * SDK has changed.
+ */
+ public void onSdkChanged() {
+ setUiElementNode(getManifestUiNode(getEditor()));
+ createUiAttributes(mManagedForm);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java
new file mode 100644
index 000000000..f8213753a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewLinksPart.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+/**
+ * Links section part for overview page.
+ */
+final class OverviewLinksPart extends ManifestSectionPart {
+
+ private final ManifestEditor mEditor;
+ private FormText mFormText;
+
+ public OverviewLinksPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
+ super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
+ mEditor = editor;
+ Section section = getSection();
+ section.setText("Links");
+ section.setDescription("The content of the Android Manifest is made up of three sections. You can also edit the XML directly.");
+
+ Composite table = createTableLayout(toolkit, 2 /* numColumns */);
+
+ StringBuffer buf = new StringBuffer();
+ buf.append(String.format("<form><li style=\"image\" value=\"app_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
+ ApplicationPage.PAGE_ID));
+ buf.append("Application");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": Activities, intent filters, providers, services and receivers.");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append(String.format("<li style=\"image\" value=\"perm_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
+ PermissionPage.PAGE_ID));
+ buf.append("Permission");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": Permissions defined and permissions used.");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append(String.format("<li style=\"image\" value=\"inst_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
+ InstrumentationPage.PAGE_ID));
+ buf.append("Instrumentation");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": Instrumentation defined.");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append(String.format("<li style=\"image\" value=\"srce_img\"><a href=\"page:%1$s\">", //$NON-NLS-1$
+ ManifestEditor.TEXT_EDITOR_ID));
+ buf.append("XML Source");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": Directly edit the AndroidManifest.xml file.");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append("<li style=\"image\" value=\"android_img\">"); //$NON-NLS-1$
+ buf.append("<a href=\"http://code.google.com/android/devel/bblocks-manifest.html\">Documentation</a>: Documentation from the Android SDK for AndroidManifest.xml."); //$NON-NLS-1$
+ buf.append("</li>"); //$NON-NLS-1$
+ buf.append("</form>"); //$NON-NLS-1$
+
+ mFormText = createFormText(table, toolkit, true, buf.toString(),
+ false /* setupLayoutData */);
+
+ AndroidManifestDescriptors manifestDescriptor = editor.getManifestDescriptors();
+
+ Image androidLogo = AdtPlugin.getAndroidLogo();
+ mFormText.setImage("android_img", androidLogo); //$NON-NLS-1$
+ mFormText.setImage("srce_img", IconFactory.getInstance().getIcon(AndroidXmlEditor.ICON_XML_PAGE));
+
+ if (manifestDescriptor != null) {
+ mFormText.setImage("app_img", getIcon(manifestDescriptor.getApplicationElement())); //$NON-NLS-1$
+ mFormText.setImage("perm_img", getIcon(manifestDescriptor.getPermissionElement())); //$NON-NLS-1$
+ mFormText.setImage("inst_img", getIcon(manifestDescriptor.getInstrumentationElement())); //$NON-NLS-1$
+ } else {
+ mFormText.setImage("app_img", androidLogo); //$NON-NLS-1$
+ mFormText.setImage("perm_img", androidLogo); //$NON-NLS-1$
+ mFormText.setImage("inst_img", androidLogo); //$NON-NLS-1$
+ }
+ mFormText.addHyperlinkListener(editor.createHyperlinkListener());
+ }
+
+ /**
+ * Update the UI with information from the new descriptors.
+ * <p/>At this point, this only refreshes the icons.
+ * <p/>
+ * This is called by {@link OverviewPage#refreshUiApplicationNode()} when the
+ * SDK has changed.
+ */
+ public void onSdkChanged() {
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+ if (manifestDescriptor != null) {
+ mFormText.setImage("app_img", getIcon(manifestDescriptor.getApplicationElement())); //$NON-NLS-1$
+ mFormText.setImage("perm_img", getIcon(manifestDescriptor.getPermissionElement())); //$NON-NLS-1$
+ mFormText.setImage("inst_img", getIcon(manifestDescriptor.getInstrumentationElement())); //$NON-NLS-1$
+ }
+ }
+
+ private Image getIcon(ElementDescriptor desc) {
+ return desc.getCustomizedIcon();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewPage.java
new file mode 100644
index 000000000..7464f6a5f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/OverviewPage.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+/**
+ * Page for overview settings, part of the AndroidManifest form editor.
+ * <p/>
+ * Useful reference:
+ * <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
+ * http://www.eclipse.org/articles/Article-Forms/article.html</a>
+ */
+public final class OverviewPage extends FormPage implements IPageImageProvider {
+
+ /** Page id used for switching tabs programmatically */
+ final static String PAGE_ID = "overview_page"; //$NON-NLS-1$
+
+ /** Container editor */
+ ManifestEditor mEditor;
+ /** Overview part (attributes for manifest) */
+ private OverviewInfoPart mOverviewPart;
+ /** Overview link part */
+ private OverviewLinksPart mOverviewLinkPart;
+
+ private UiTreeBlock mTreeBlock;
+
+ public OverviewPage(ManifestEditor editor) {
+ super(editor, PAGE_ID, "Manifest"); // tab's label, user visible, keep it short
+ mEditor = editor;
+ }
+
+ @Override
+ public Image getPageImage() {
+ return IconFactory.getInstance().getIcon("editor_page_design"); //$NON-NLS-1$
+ }
+
+ /**
+ * Creates the content in the form hosted in this page.
+ *
+ * @param managedForm the form hosted in this page.
+ */
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ super.createFormContent(managedForm);
+ ScrolledForm form = managedForm.getForm();
+ form.setText("Android Manifest");
+ form.setImage(AdtPlugin.getAndroidLogo());
+
+ Composite body = form.getBody();
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ // Usually we would set a ColumnLayout on body here. However the presence of the
+ // UiTreeBlock forces a GridLayout with one column so we comply with it.
+
+ mOverviewPart = new OverviewInfoPart(body, toolkit, mEditor);
+ mOverviewPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ managedForm.addPart(mOverviewPart);
+
+ newManifestExtrasPart(managedForm);
+
+ OverviewExportPart exportPart = new OverviewExportPart(this, body, toolkit, mEditor);
+ exportPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ managedForm.addPart(exportPart);
+
+ mOverviewLinkPart = new OverviewLinksPart(body, toolkit, mEditor);
+ mOverviewLinkPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ managedForm.addPart(mOverviewLinkPart);
+ }
+
+ private void newManifestExtrasPart(IManagedForm managedForm) {
+ UiElementNode manifest = mEditor.getUiRootNode();
+ mTreeBlock = new UiTreeBlock(mEditor, manifest,
+ true /* autoCreateRoot */,
+ computeManifestExtraFilters(),
+ "Manifest Extras",
+ "Extra manifest elements");
+ mTreeBlock.createContent(managedForm);
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handled by the sub parts.
+ */
+ public void refreshUiApplicationNode() {
+ if (mOverviewPart != null) {
+ mOverviewPart.onSdkChanged();
+ }
+
+ if (mOverviewLinkPart != null) {
+ mOverviewLinkPart.onSdkChanged();
+ }
+
+ if (mTreeBlock != null) {
+ UiElementNode manifest = mEditor.getUiRootNode();
+ mTreeBlock.changeRootAndDescriptors(manifest,
+ computeManifestExtraFilters(),
+ true /* refresh */);
+ }
+ }
+
+ private ElementDescriptor[] computeManifestExtraFilters() {
+ UiElementNode manifest = mEditor.getUiRootNode();
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+
+ if (manifestDescriptor == null) {
+ return null;
+ }
+
+ // get the elements we want to exclude
+ HashSet<ElementDescriptor> excludes = new HashSet<ElementDescriptor>();
+ excludes.add(manifestDescriptor.getApplicationElement());
+ excludes.add(manifestDescriptor.getInstrumentationElement());
+ excludes.add(manifestDescriptor.getPermissionElement());
+ excludes.add(manifestDescriptor.getPermissionGroupElement());
+ excludes.add(manifestDescriptor.getPermissionTreeElement());
+ excludes.add(manifestDescriptor.getUsesPermissionElement());
+
+ // walk through the known children of the manifest descriptor and keep what's not excluded
+ ArrayList<ElementDescriptor> descriptorFilters = new ArrayList<ElementDescriptor>();
+ for (ElementDescriptor child : manifest.getDescriptor().getChildren()) {
+ if (!excludes.contains(child)) {
+ descriptorFilters.add(child);
+ }
+ }
+
+ if (descriptorFilters.size() == 0) {
+ return null;
+ }
+ return descriptorFilters.toArray(new ElementDescriptor[descriptorFilters.size()]);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/PermissionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/PermissionPage.java
new file mode 100644
index 000000000..2f655777a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/pages/PermissionPage.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 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.manifest.pages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * Page for permissions settings, part of the AndroidManifest form editor.
+ * <p/>
+ * Useful reference:
+ * <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
+ * http://www.eclipse.org/articles/Article-Forms/article.html</a>
+ */
+public final class PermissionPage extends FormPage implements IPageImageProvider {
+ /** Page id used for switching tabs programmatically */
+ public final static String PAGE_ID = "permission_page"; //$NON-NLS-1$
+
+ /** Container editor */
+ ManifestEditor mEditor;
+
+ private UiTreeBlock mTreeBlock;
+
+ public PermissionPage(ManifestEditor editor) {
+ super(editor, PAGE_ID, "Permissions"); // tab label, keep it short
+ mEditor = editor;
+ }
+
+ @Override
+ public Image getPageImage() {
+ return IconFactory.getInstance().getIcon(getTitle(),
+ IconFactory.COLOR_RED,
+ IconFactory.SHAPE_RECT);
+ }
+
+ /**
+ * Creates the content in the form hosted in this page.
+ *
+ * @param managedForm the form hosted in this page.
+ */
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ super.createFormContent(managedForm);
+ ScrolledForm form = managedForm.getForm();
+ form.setText("Android Manifest Permissions");
+ form.setImage(AdtPlugin.getAndroidLogo());
+
+ UiElementNode manifest = mEditor.getUiRootNode();
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+
+ ElementDescriptor[] descriptorFilters = null;
+ if (manifestDescriptor != null) {
+ descriptorFilters = new ElementDescriptor[] {
+ manifestDescriptor.getPermissionElement(),
+ manifestDescriptor.getUsesPermissionElement(),
+ manifestDescriptor.getPermissionGroupElement(),
+ manifestDescriptor.getPermissionTreeElement()
+ };
+ }
+ mTreeBlock = new UiTreeBlock(mEditor, manifest,
+ true /* autoCreateRoot */,
+ descriptorFilters,
+ "Permissions",
+ "List of permissions defined and used by the manifest");
+ mTreeBlock.createContent(managedForm);
+ }
+
+ /**
+ * Changes and refreshes the Application UI node handled by the sub parts.
+ */
+ public void refreshUiNode() {
+ if (mTreeBlock != null) {
+ UiElementNode manifest = mEditor.getUiRootNode();
+ AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors();
+
+ mTreeBlock.changeRootAndDescriptors(manifest,
+ new ElementDescriptor[] {
+ manifestDescriptor.getPermissionElement(),
+ manifestDescriptor.getUsesPermissionElement(),
+ manifestDescriptor.getPermissionGroupElement(),
+ manifestDescriptor.getPermissionTreeElement()
+ },
+ true /* refresh */);
+ }
+ }
+}