Code drop from //branches/cupcake/...@124589
@@ -19,11 +19,6 @@
|
||||
<property name="id" value="com.android.ide.eclipse.adt" />
|
||||
</ant>
|
||||
|
||||
<ant antfile="${genericTargets}" target="${target}">
|
||||
<property name="type" value="feature" />
|
||||
<property name="id" value="com.android.ide.eclipse.editors" />
|
||||
</ant>
|
||||
|
||||
<antcall target="buildInternalFeatures"/>
|
||||
|
||||
</target>
|
||||
@@ -33,17 +28,12 @@
|
||||
<!-- Builds if property internalSite is set -->
|
||||
<!-- ===================================================================== -->
|
||||
<target name="buildInternalFeatures" if="internalSite">
|
||||
<ant antfile="${genericTargets}" target="${target}">
|
||||
<property name="type" value="feature" />
|
||||
<property name="id" value="com.android.ide.eclipse.platform" />
|
||||
</ant>
|
||||
<ant antfile="${genericTargets}" target="${target}">
|
||||
<property name="type" value="feature" />
|
||||
<property name="id" value="com.android.ide.eclipse.tests" />
|
||||
</ant>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Targets to assemble the built elements for particular configurations -->
|
||||
<!-- These generally call the generated assemble scripts (named in -->
|
||||
@@ -55,14 +45,6 @@
|
||||
<ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
|
||||
</target>
|
||||
|
||||
<target name="assemble.com.android.ide.eclipse.editors">
|
||||
<ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
|
||||
</target>
|
||||
|
||||
<target name="assemble.com.android.ide.eclipse.platform">
|
||||
<ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
|
||||
</target>
|
||||
|
||||
<target name="assemble.com.android.ide.eclipse.tests">
|
||||
<ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
|
||||
</target>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
0.9.0 (work in progress)
|
||||
- Support for SDK with multiple versions of the Android platform and vendor supplied add-ons.
|
||||
|
||||
0.8.1:
|
||||
|
||||
- Alternate Layout wizard. In the layout editor, the "create" button is now enabled, and allows to easily create alternate versions.
|
||||
@@ -5,6 +8,8 @@
|
||||
- Export Wizard: To export an application for release, sign with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor.
|
||||
- New XML File Wizard: To easily create new XML resources file in the /res directory.
|
||||
- New checks on launch when attempting to debug on a device.
|
||||
- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There's is no support for moving/resizing yet.
|
||||
- Undo/redo support in all XML form editors and Graphical layout editor.
|
||||
|
||||
0.8.0:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="com.android.ide.eclipse.adt"
|
||||
label="Android Development Tools"
|
||||
version="0.8.1.qualifier"
|
||||
version="0.9.0.qualifier"
|
||||
provider-name="The Android Open Source Project"
|
||||
plugin="com.android.ide.eclipse.adt">
|
||||
|
||||
@@ -14,36 +14,34 @@
|
||||
Copyright (C) 2007 The Android Open Source Project
|
||||
</copyright>
|
||||
|
||||
<license>
|
||||
License TBD.
|
||||
</license>
|
||||
|
||||
<url>
|
||||
<update label="Android Update Site" url="https://dl-ssl.google.com/android/eclipse/"/>
|
||||
</url>
|
||||
|
||||
<requires>
|
||||
<import plugin="org.eclipse.core.resources"/>
|
||||
<import plugin="org.eclipse.core.runtime"/>
|
||||
<import plugin="org.eclipse.jdt.core"/>
|
||||
<import plugin="org.eclipse.ui.console"/>
|
||||
<import plugin="org.eclipse.ui"/>
|
||||
<import plugin="org.eclipse.jdt.ui"/>
|
||||
<import plugin="org.eclipse.jface.text"/>
|
||||
<import plugin="org.eclipse.ui.editors"/>
|
||||
<import plugin="org.eclipse.core.resources"/>
|
||||
<import plugin="org.eclipse.debug.core"/>
|
||||
<import plugin="org.eclipse.debug.ui"/>
|
||||
<import plugin="org.eclipse.jdt"/>
|
||||
<import plugin="org.eclipse.ant.core"/>
|
||||
<import plugin="org.eclipse.jdt.core"/>
|
||||
<import plugin="org.eclipse.jdt.ui"/>
|
||||
<import plugin="org.eclipse.jdt.launching"/>
|
||||
<import plugin="org.eclipse.jface.text"/>
|
||||
<import plugin="org.eclipse.ui.editors"/>
|
||||
<import plugin="org.eclipse.ui.workbench.texteditor"/>
|
||||
<import plugin="org.eclipse.ui.console"/>
|
||||
<import plugin="org.eclipse.core.filesystem"/>
|
||||
<import plugin="org.eclipse.ui"/>
|
||||
<import plugin="org.eclipse.ui.ide"/>
|
||||
<import plugin="org.eclipse.ui.forms"/>
|
||||
</requires>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.common"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.adt"
|
||||
download-size="0"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>editors-feature</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1 +0,0 @@
|
||||
bin.includes = feature.xml
|
||||
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feature
|
||||
id="com.android.ide.eclipse.editors"
|
||||
label="Android Editors"
|
||||
version="0.8.1.qualifier"
|
||||
provider-name="The Android Open Source Project"
|
||||
plugin="com.android.ide.eclipse.editors">
|
||||
|
||||
<description>
|
||||
This feature provides Editors for Android files.
|
||||
</description>
|
||||
|
||||
<copyright>
|
||||
Copyright (C) 2007 The Android Open Source Project
|
||||
</copyright>
|
||||
|
||||
<url>
|
||||
<update label="Android Update Site" url="https://dl-ssl.google.com/android/eclipse/"/>
|
||||
</url>
|
||||
|
||||
<requires>
|
||||
<import plugin="com.android.ide.eclipse.common"/>
|
||||
<import plugin="org.eclipse.ui"/>
|
||||
<import plugin="org.eclipse.core.runtime"/>
|
||||
<import plugin="org.eclipse.core.resources"/>
|
||||
<import plugin="org.eclipse.ui.editors"/>
|
||||
<import plugin="org.eclipse.jface.text"/>
|
||||
<import plugin="org.eclipse.ui.ide"/>
|
||||
<import plugin="org.eclipse.wst.sse.ui"/>
|
||||
<import plugin="org.eclipse.wst.xml.ui"/>
|
||||
<import plugin="org.eclipse.wst.xml.core"/>
|
||||
<import plugin="org.eclipse.wst.sse.core"/>
|
||||
<import plugin="org.eclipse.ui.forms"/>
|
||||
<import plugin="org.eclipse.jdt.core"/>
|
||||
<import plugin="org.eclipse.ui.browser"/>
|
||||
<import plugin="org.eclipse.jdt.ui"/>
|
||||
<import plugin="org.eclipse.gef"/>
|
||||
<import plugin="org.eclipse.ui.views"/>
|
||||
<import plugin="org.eclipse.ui.console"/>
|
||||
</requires>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.editors"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
</feature>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>platform-feature</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1 +0,0 @@
|
||||
bin.includes = feature.xml
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feature
|
||||
id="com.android.ide.eclipse.platform"
|
||||
label="Android Platform Tools"
|
||||
version="0.8.1.qualifier"
|
||||
provider-name="The Android Open Source Project">
|
||||
|
||||
<description>
|
||||
This feature provides a ddms perspective and editor functionnality for Android.
|
||||
</description>
|
||||
|
||||
<copyright>
|
||||
Copyright (C) 2007 The Android Open Source Project
|
||||
</copyright>
|
||||
|
||||
<url>
|
||||
<update label="Android Update Site" url="https://android.corp.google.com/adt/"/>
|
||||
</url>
|
||||
|
||||
<requires>
|
||||
<import plugin="org.eclipse.core.resources"/>
|
||||
<import plugin="org.eclipse.core.runtime"/>
|
||||
<import plugin="org.eclipse.jdt.core"/>
|
||||
<import plugin="org.eclipse.ui.console"/>
|
||||
<import plugin="org.eclipse.ui"/>
|
||||
<import plugin="org.eclipse.jdt.ui"/>
|
||||
<import plugin="org.eclipse.jface.text"/>
|
||||
<import plugin="org.eclipse.ui.editors"/>
|
||||
</requires>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.common"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.platform"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.android.ide.eclipse.ddms"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
</feature>
|
||||
@@ -2,11 +2,11 @@
|
||||
<feature
|
||||
id="com.android.ide.eclipse.tests"
|
||||
label="ADT Tests"
|
||||
version="0.8.1.qualifier"
|
||||
version="0.9.0.qualifier"
|
||||
provider-name="The Android Open Source Project">
|
||||
|
||||
<copyright>
|
||||
Copyright (C) 2007 The Android Open Source Project
|
||||
<copyright>
|
||||
Copyright (C) 2007 The Android Open Source Project
|
||||
</copyright>
|
||||
|
||||
<requires>
|
||||
@@ -15,8 +15,6 @@
|
||||
<import plugin="org.eclipse.core.resources"/>
|
||||
<import plugin="com.android.ide.eclipse.adt"/>
|
||||
<import plugin="org.junit"/>
|
||||
<import plugin="com.android.ide.eclipse.common"/>
|
||||
<import plugin="com.android.ide.eclipse.editors"/>
|
||||
<import plugin="org.eclipse.jdt.core"/>
|
||||
<import plugin="org.eclipse.jdt.launching"/>
|
||||
<import plugin="org.eclipse.ui.views"/>
|
||||
|
||||
@@ -5,5 +5,12 @@
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="lib" path="jarutils.jar"/>
|
||||
<classpathentry kind="lib" path="androidprefs.jar"/>
|
||||
<classpathentry kind="lib" path="sdkstats.jar"/>
|
||||
<classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
|
||||
<classpathentry kind="lib" path="layoutlib_api.jar"/>
|
||||
<classpathentry kind="lib" path="layoutlib_utils.jar"/>
|
||||
<classpathentry kind="lib" path="ninepatch.jar"/>
|
||||
<classpathentry kind="lib" path="sdklib.jar"/>
|
||||
<classpathentry kind="lib" path="sdkuilib.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -2,14 +2,20 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Android Development Toolkit
|
||||
Bundle-SymbolicName: com.android.ide.eclipse.adt;singleton:=true
|
||||
Bundle-Version: 0.8.1.qualifier
|
||||
Bundle-Version: 0.9.0.qualifier
|
||||
Bundle-ClassPath: .,
|
||||
jarutils.jar,
|
||||
androidprefs.jar
|
||||
androidprefs.jar,
|
||||
sdkstats.jar,
|
||||
kxml2-2.3.0.jar,
|
||||
layoutlib_api.jar,
|
||||
ninepatch.jar,
|
||||
layoutlib_utils.jar,
|
||||
sdklib.jar,
|
||||
sdkuilib.jar
|
||||
Bundle-Activator: com.android.ide.eclipse.adt.AdtPlugin
|
||||
Bundle-Vendor: The Android Open Source Project
|
||||
Require-Bundle: com.android.ide.eclipse.common,
|
||||
com.android.ide.eclipse.ddms,
|
||||
Require-Bundle: com.android.ide.eclipse.ddms,
|
||||
org.eclipse.core.runtime,
|
||||
org.eclipse.core.resources,
|
||||
org.eclipse.debug.core,
|
||||
@@ -26,11 +32,48 @@ Require-Bundle: com.android.ide.eclipse.common,
|
||||
org.eclipse.core.filesystem,
|
||||
org.eclipse.ui,
|
||||
org.eclipse.ui.ide,
|
||||
org.eclipse.ui.forms
|
||||
org.eclipse.ui.forms,
|
||||
org.eclipse.gef,
|
||||
org.eclipse.ui.browser,
|
||||
org.eclipse.ui.views,
|
||||
org.eclipse.wst.sse.core,
|
||||
org.eclipse.wst.sse.ui,
|
||||
org.eclipse.wst.xml.core,
|
||||
org.eclipse.wst.xml.ui
|
||||
Eclipse-LazyStart: true
|
||||
Export-Package: com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
|
||||
Export-Package: com.android.ide.eclipse.adt,
|
||||
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.project;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.resources;x-friends:="com.android.ide.eclipse.tests"
|
||||
com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.common,
|
||||
com.android.ide.eclipse.common.project,
|
||||
com.android.ide.eclipse.common.resources,
|
||||
com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.model;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.pages;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.menu;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.menu.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.configurations;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.explorer;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.manager;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.manager.files;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests"
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,13 @@ bin.includes = plugin.xml,\
|
||||
templates/,\
|
||||
about.ini,\
|
||||
jarutils.jar,\
|
||||
androidprefs.jar
|
||||
androidprefs.jar,\
|
||||
sdkstats.jar,\
|
||||
kxml2-2.3.0.jar,\
|
||||
layoutlib_api.jar,\
|
||||
layoutlib_utils.jar,\
|
||||
ninepatch.jar,\
|
||||
sdklib.jar,\
|
||||
sdkuilib.jar
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
|
||||
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 107 B |
|
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 320 B |
|
Before Width: | Height: | Size: 157 B After Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 138 B |
BIN
tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/mcc.png
Normal file
|
After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 325 B |
|
After Width: | Height: | Size: 445 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 137 B After Width: | Height: | Size: 137 B |
|
Before Width: | Height: | Size: 147 B After Width: | Height: | Size: 147 B |
@@ -1,6 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.common.xmlProblem"
|
||||
name="Android XML Format Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<super type="org.eclipse.core.resources.textmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.common.aaptProblem"
|
||||
name="Android AAPT Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<super type="org.eclipse.core.resources.textmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.common.aidlProblem"
|
||||
name="Android AIDL Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<super type="org.eclipse.core.resources.textmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.common.androidProblem"
|
||||
name="Android XML Content Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<super type="org.eclipse.core.resources.textmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="ResourceManagerBuilder"
|
||||
name="Android Resource Manager"
|
||||
@@ -47,7 +79,7 @@
|
||||
<wizard
|
||||
canFinishEarly="false"
|
||||
category="com.android.ide.eclipse.wizards.category"
|
||||
class="com.android.ide.eclipse.adt.project.internal.NewProjectWizard"
|
||||
class="com.android.ide.eclipse.adt.wizards.newproject.NewProjectWizard"
|
||||
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
|
||||
hasPages="true"
|
||||
icon="icons/android.png"
|
||||
@@ -55,6 +87,18 @@
|
||||
name="Android Project"
|
||||
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
|
||||
project="true"/>
|
||||
<wizard
|
||||
canFinishEarly="false"
|
||||
category="com.android.ide.eclipse.wizards.category"
|
||||
class="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
|
||||
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
|
||||
hasPages="true"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
|
||||
name="Android XML File"
|
||||
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
|
||||
project="false">
|
||||
</wizard>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.debug.core.launchConfigurationTypes">
|
||||
@@ -173,6 +217,13 @@
|
||||
id="com.android.ide.eclipse.adt.project.CreateAidlImportAction"
|
||||
label="Create Aidl preprocess file for Parcelable classes"
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1"/>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction"
|
||||
label="New Resource File..."
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1">
|
||||
</action>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.ExportAction"
|
||||
enablesFor="1"
|
||||
@@ -183,7 +234,7 @@
|
||||
class="com.android.ide.eclipse.adt.project.ExportWizardAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.project.ExportWizardAction"
|
||||
label="Export Application..."
|
||||
label="Export Signed Application Package..."
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group2"/>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.FixProjectAction"
|
||||
@@ -209,29 +260,31 @@
|
||||
class="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage"
|
||||
id="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage"
|
||||
name="Launch"/>
|
||||
<page
|
||||
category="com.android.ide.eclipse.preferences.main"
|
||||
class="com.android.ide.eclipse.common.preferences.UsagePreferencePage"
|
||||
id="com.android.ide.eclipse.common.preferences.UsagePreferencePage"
|
||||
name="Usage Stats">
|
||||
</page>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.core.runtime.preferences">
|
||||
<initializer class="com.android.ide.eclipse.adt.preferences.PreferenceInitializer"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editor
|
||||
class="com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor"
|
||||
contributorClass="org.eclipse.ui.texteditor.BasicTextEditorActionContributor"
|
||||
default="true"
|
||||
filenames="R.java, Manifest.java"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor"
|
||||
name="Android Java Editor"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.adt.adtProblem"
|
||||
name="Generic ADT Problem"
|
||||
name="Android ADT Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
id="com.android.ide.eclipse.adt.targetProblem"
|
||||
name="Android Target Problem"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<persistent value="false"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.perspectiveExtensions">
|
||||
<perspectiveExtension targetID="org.eclipse.jdt.ui.JavaPerspective">
|
||||
@@ -305,4 +358,110 @@
|
||||
keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration">
|
||||
</keyBinding>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.decorators">
|
||||
<decorator
|
||||
adaptable="true"
|
||||
class="com.android.ide.eclipse.adt.project.FolderDecorator"
|
||||
id="com.android.ide.eclipse.adt.project.FolderDecorator"
|
||||
label="Android Decorator"
|
||||
lightweight="true"
|
||||
location="TOP_RIGHT"
|
||||
objectClass="org.eclipse.core.resources.IFolder"
|
||||
state="true">
|
||||
</decorator>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.manifest.ManifestEditor"
|
||||
default="true"
|
||||
filenames="AndroidManifest.xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.manifest.ManifestEditor"
|
||||
name="Android Manifest Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.resources.ResourcesEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.resources.ResourcesEditor"
|
||||
name="Android Resource Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.layout.LayoutEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.layout.LayoutEditor"
|
||||
matchingStrategy="com.android.ide.eclipse.editors.layout.MatchingStrategy"
|
||||
name="Android Layout Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.menu.MenuEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.menu.MenuEditor"
|
||||
name="Android Menu Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.xml.XmlEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.xml.XmlEditor"
|
||||
name="Android Xml Resources Editor">
|
||||
</editor>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.views">
|
||||
<view
|
||||
allowMultiple="false"
|
||||
category="com.android.ide.eclipse.ddms.views.category"
|
||||
class="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
|
||||
name="Resource Explorer">
|
||||
</view>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.wst.sse.ui.editorConfiguration">
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.manifest.ManifestEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.resources.ResourcesEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.layout.LayoutEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.menu.MenuEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.xml.XmlEditor">
|
||||
</sourceViewerConfiguration>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.propertyPages">
|
||||
<page
|
||||
adaptable="true"
|
||||
class="com.android.ide.eclipse.adt.project.properties.AndroidPropertyPage"
|
||||
id="com.android.ide.eclipse.adt.project.properties.AndroidPropertyPage"
|
||||
name="Android"
|
||||
nameFilter="*"
|
||||
objectClass="org.eclipse.core.resources.IProject">
|
||||
<enabledWhen>
|
||||
<test property="org.eclipse.jdt.launching.hasProjectNature"
|
||||
args="com.android.ide.eclipse.adt.AndroidNature"/>
|
||||
</enabledWhen>
|
||||
</page>
|
||||
</extension>
|
||||
</plugin>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.ide.eclipse.adt;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constant definition class.<br>
|
||||
* <br>
|
||||
@@ -39,6 +40,12 @@ public class AdtConstants {
|
||||
/** Generic marker for ADT errors. */
|
||||
public final static String MARKER_ADT = AdtPlugin.PLUGIN_ID + ".adtProblem"; //$NON-NLS-1$
|
||||
|
||||
/** Marker for Android Target errors.
|
||||
* This is not cleared on each like other markers. Instead, it's cleared
|
||||
* when a ContainerClasspathInitialized has succeeded in creating an
|
||||
* {@link AndroidClasspathContainer}*/
|
||||
public final static String MARKER_TARGET = AdtPlugin.PLUGIN_ID + ".targetProblem"; //$NON-NLS-1$
|
||||
|
||||
/** Build verbosity "Always". Those messages are always displayed. */
|
||||
public final static int BUILD_ALWAYS = 0;
|
||||
|
||||
|
||||
@@ -22,34 +22,55 @@ import com.android.ddmuilib.console.DdmConsole;
|
||||
import com.android.ddmuilib.console.IDdmConsole;
|
||||
import com.android.ide.eclipse.adt.build.DexWrapper;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController;
|
||||
import com.android.ide.eclipse.adt.debug.ui.SkinRepository;
|
||||
import com.android.ide.eclipse.adt.preferences.BuildPreferencePage;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard;
|
||||
import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
|
||||
import com.android.ide.eclipse.adt.resources.FrameworkResourceParser;
|
||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetParser;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.CommonPlugin;
|
||||
import com.android.ide.eclipse.common.EclipseUiHelper;
|
||||
import com.android.ide.eclipse.common.SdkStatsHelper;
|
||||
import com.android.ide.eclipse.common.StreamHelper;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.ExportHelper;
|
||||
import com.android.ide.eclipse.common.project.ExportHelper.IExportCallback;
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||
import com.android.ide.eclipse.ddms.ImageLoader;
|
||||
import com.android.ide.eclipse.editors.IconFactory;
|
||||
import com.android.ide.eclipse.editors.layout.LayoutEditor;
|
||||
import com.android.ide.eclipse.editors.menu.MenuEditor;
|
||||
import com.android.ide.eclipse.editors.resources.ResourcesEditor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
|
||||
import com.android.ide.eclipse.editors.xml.XmlEditor;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IMarkerDelta;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.core.runtime.Preferences;
|
||||
import org.eclipse.core.runtime.QualifiedName;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
|
||||
import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
|
||||
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
@@ -57,14 +78,21 @@ import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.jface.viewers.StructuredSelection;
|
||||
import org.eclipse.jface.wizard.WizardDialog;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.IEditorDescriptor;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPage;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.console.ConsolePlugin;
|
||||
import org.eclipse.ui.console.IConsole;
|
||||
import org.eclipse.ui.console.IConsoleConstants;
|
||||
import org.eclipse.ui.console.MessageConsole;
|
||||
import org.eclipse.ui.console.MessageConsoleStream;
|
||||
import org.eclipse.ui.ide.IDE;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
import org.eclipse.ui.plugin.AbstractUIPlugin;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
@@ -81,6 +109,8 @@ import java.io.PrintStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The activator class controls the plug-in life cycle
|
||||
@@ -106,14 +136,17 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
/** singleton instance */
|
||||
private static AdtPlugin sPlugin;
|
||||
|
||||
private static Image sAndroidLogo;
|
||||
private static ImageDescriptor sAndroidLogoDesc;
|
||||
|
||||
/** default store, provided by eclipse */
|
||||
private IPreferenceStore mStore;
|
||||
|
||||
/** cached location for the sdk folder */
|
||||
private String mOsSdkLocation;
|
||||
|
||||
/** SDK Api Version */
|
||||
String mSdkApiVersion;
|
||||
/** The global android console */
|
||||
private MessageConsole mAndroidConsole;
|
||||
|
||||
/** Stream to write in the android console */
|
||||
private MessageConsoleStream mAndroidConsoleStream;
|
||||
@@ -130,14 +163,14 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
/** Color used in the error console */
|
||||
private Color mRed;
|
||||
|
||||
private final ArrayList<IJavaProject> mPostDexProjects = new ArrayList<IJavaProject>();
|
||||
/** Load status of the SDK. Any access MUST be in a synchronized(mPostLoadProjects) block */
|
||||
private LoadStatus mSdkIsLoaded = LoadStatus.LOADING;
|
||||
/** Project to update once the SDK is loaded.
|
||||
* Any access MUST be in a synchronized(mPostLoadProjects) block */
|
||||
private final ArrayList<IJavaProject> mPostLoadProjects = new ArrayList<IJavaProject>();
|
||||
|
||||
/** Boolean wrapper to run dialog in the UI thread, and still get the
|
||||
* return code.
|
||||
*/
|
||||
private static final class BooleanWrapper {
|
||||
public boolean b;
|
||||
}
|
||||
private ResourceMonitor mResourceMonitor;
|
||||
private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>();
|
||||
|
||||
/**
|
||||
* Custom PrintStream for Dx output. This class overrides the method
|
||||
@@ -210,10 +243,14 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
Display display = getDisplay();
|
||||
|
||||
// set the default android console.
|
||||
mAndroidConsole = new MessageConsole("Android", null); //$NON-NLS-1$
|
||||
ConsolePlugin.getDefault().getConsoleManager().addConsoles(
|
||||
new IConsole[] { mAndroidConsole });
|
||||
|
||||
// get the stream to write in the android console.
|
||||
MessageConsole androidConsole = CommonPlugin.getDefault().getAndroidConsole();
|
||||
mAndroidConsoleStream = androidConsole.newMessageStream();
|
||||
mAndroidConsoleErrorStream = androidConsole.newMessageStream();
|
||||
mAndroidConsoleStream = mAndroidConsole.newMessageStream();
|
||||
mAndroidConsoleErrorStream = mAndroidConsole.newMessageStream();
|
||||
mRed = new Color(display, 0xFF, 0x00, 0x00);
|
||||
|
||||
// because this can be run, in some cases, by a non ui thread, and beccause
|
||||
@@ -267,22 +304,19 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
// get the SDK location and build id.
|
||||
if (checkSdkLocationAndId()) {
|
||||
// if sdk if valid, reparse the skin folder
|
||||
SkinRepository.getInstance().parseFolder(getOsSkinFolder());
|
||||
// if sdk if valid, reparse it
|
||||
|
||||
// add the current Android project to the list of projects to be updated
|
||||
// after the SDK is reloaded
|
||||
synchronized (mPostLoadProjects) {
|
||||
// get the project to refresh.
|
||||
IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects();
|
||||
mPostLoadProjects.addAll(Arrays.asList(androidProjects));
|
||||
}
|
||||
|
||||
// parse the SDK resources at the new location
|
||||
parseSdkContent();
|
||||
}
|
||||
|
||||
// parse the SDK resources at the new location
|
||||
parseSdkContent();
|
||||
|
||||
// get the project to refresh.
|
||||
IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects();
|
||||
|
||||
// Setup the new container for each project. By providing new instances of
|
||||
// AndroidClasspathContainer, this will force JDT to call
|
||||
// IClasspathContainer#getClasspathEntries() again and receive the new
|
||||
// path to the framework jar.
|
||||
AndroidClasspathContainerInitializer.updateProjects(androidProjects);
|
||||
|
||||
} else if (PREFS_BUILD_VERBOSITY.equals(property)) {
|
||||
mBuildVerbosity = BuildPreferencePage.getBuildLevel(
|
||||
mStore.getString(PREFS_BUILD_VERBOSITY));
|
||||
@@ -299,20 +333,11 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
}
|
||||
|
||||
// check the location of SDK
|
||||
if (checkSdkLocationAndId()) {
|
||||
// if sdk if valid, parse the skin folder
|
||||
SkinRepository.getInstance().parseFolder(getOsSkinFolder());
|
||||
|
||||
// parse the SDK resources.
|
||||
parseSdkContent();
|
||||
}
|
||||
final boolean isSdkLocationValid = checkSdkLocationAndId();
|
||||
|
||||
mBuildVerbosity = BuildPreferencePage.getBuildLevel(
|
||||
mStore.getString(PREFS_BUILD_VERBOSITY));
|
||||
|
||||
// Ping the usage start server.
|
||||
pingUsageServer();
|
||||
|
||||
// create the loader that's able to load the images
|
||||
mLoader = new ImageLoader(this);
|
||||
|
||||
@@ -356,18 +381,31 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
dialog.open();
|
||||
}
|
||||
});
|
||||
|
||||
// initialize editors
|
||||
startEditors();
|
||||
|
||||
/* The Editors plugin must be started as soon as Android projects are opened or created,
|
||||
* in order to properly set default editors on the layout/values XML files.
|
||||
*
|
||||
* This ensures that the default editors is really only set when a new XML file
|
||||
* is added to the workspace (IResourceDelta.ADDED event), through project creation or
|
||||
* manual add.
|
||||
* Other methods would force to go through existing projects when the Editors plugin is
|
||||
* started, and set the default editors for their XML files, possibly erasing user set
|
||||
* default editors.
|
||||
*/
|
||||
startEditorsPlugin();
|
||||
// Ping the usage server and parse the SDK content.
|
||||
// This is deferred in separate jobs to avoid blocking the bundle start.
|
||||
// We also serialize them to avoid too many parallel jobs when Eclipse starts.
|
||||
Job pingJob = createPingUsageServerJob();
|
||||
pingJob.addJobChangeListener(new JobChangeAdapter() {
|
||||
@Override
|
||||
public void done(IJobChangeEvent event) {
|
||||
super.done(event);
|
||||
|
||||
// Once the ping job is finished, start the SDK parser
|
||||
if (isSdkLocationValid) {
|
||||
// parse the SDK resources.
|
||||
parseSdkContent();
|
||||
}
|
||||
}
|
||||
});
|
||||
// build jobs are run after other interactive jobs
|
||||
pingJob.setPriority(Job.BUILD);
|
||||
// Wait 2 seconds before starting the ping job. This leaves some time to the
|
||||
// other bundles to initialize.
|
||||
pingJob.schedule(2000 /*milliseconds*/);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -379,6 +417,8 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
super.stop(context);
|
||||
|
||||
stopEditors();
|
||||
|
||||
DexWrapper.unloadDex();
|
||||
|
||||
mRed.dispose();
|
||||
@@ -418,37 +458,22 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
/** Returns the adb path relative to the sdk folder */
|
||||
public static String getOsRelativeAdb() {
|
||||
return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
|
||||
}
|
||||
|
||||
/** Returns the aapt path relative to the sdk folder */
|
||||
public static String getOsRelativeAapt() {
|
||||
return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT;
|
||||
}
|
||||
|
||||
/** Returns the emulator path relative to the sdk folder */
|
||||
public static String getOsRelativeEmulator() {
|
||||
return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
|
||||
}
|
||||
|
||||
/** Returns the aidl path relative to the sdk folder */
|
||||
public static String getOsRelativeAidl() {
|
||||
return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL;
|
||||
}
|
||||
|
||||
/** Returns the framework jar path relative to the sdk folder */
|
||||
public static String getOsRelativeFramework() {
|
||||
return AndroidConstants.FN_FRAMEWORK_LIBRARY;
|
||||
}
|
||||
|
||||
/** Returns the android sources path relative to the sdk folder */
|
||||
public static String getOsRelativeAndroidSources() {
|
||||
return AndroidConstants.FD_ANDROID_SOURCES;
|
||||
}
|
||||
|
||||
/** Returns the framework jar path relative to the sdk folder */
|
||||
public static String getOsRelativeAttrsXml() {
|
||||
return AndroidConstants.OS_SDK_LIBS_FOLDER + AndroidConstants.FN_ATTRS_XML;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL;
|
||||
}
|
||||
|
||||
/** Returns the absolute adb path */
|
||||
@@ -458,7 +483,7 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
/** Returns the absolute traceview path */
|
||||
public static String getOsAbsoluteTraceview() {
|
||||
return getOsSdkFolder() + AndroidConstants.OS_SDK_TOOLS_FOLDER +
|
||||
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
|
||||
AndroidConstants.FN_TRACEVIEW;
|
||||
}
|
||||
|
||||
@@ -467,21 +492,6 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
return getOsSdkFolder() + getOsRelativeAapt();
|
||||
}
|
||||
|
||||
/** Returns the absolute sdk framework path */
|
||||
public static String getOsAbsoluteFramework() {
|
||||
return getOsSdkFolder() + getOsRelativeFramework();
|
||||
}
|
||||
|
||||
/** Returns the absolute android sources path in the sdk */
|
||||
public static String getOsAbsoluteAndroidSources() {
|
||||
return getOsSdkFolder() + getOsRelativeAndroidSources();
|
||||
}
|
||||
|
||||
/** Returns the absolute attrs.xml path */
|
||||
public static String getOsAbsoluteAttrsXml() {
|
||||
return getOsSdkFolder() + getOsRelativeAttrsXml();
|
||||
}
|
||||
|
||||
/** Returns the absolute emulator path */
|
||||
public static String getOsAbsoluteEmulator() {
|
||||
return getOsSdkFolder() + getOsRelativeEmulator();
|
||||
@@ -492,16 +502,6 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
return getOsSdkFolder() + getOsRelativeAidl();
|
||||
}
|
||||
|
||||
/** Returns the absolute path to the aidl framework import file. */
|
||||
public static String getOsAbsoluteFrameworkAidl() {
|
||||
return getOsSdkFolder() + AndroidConstants.OS_SDK_LIBS_FOLDER +
|
||||
AndroidConstants.FN_FRAMEWORK_AIDL;
|
||||
}
|
||||
|
||||
public static String getOsSdkSamplesFolder() {
|
||||
return getOsSdkFolder() + AndroidConstants.OS_SDK_SAMPLES_FOLDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Url file path to the javaDoc folder.
|
||||
*/
|
||||
@@ -526,11 +526,7 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
}
|
||||
|
||||
public static String getOsSdkToolsFolder() {
|
||||
return getOsSdkFolder() + AndroidConstants.OS_SDK_TOOLS_FOLDER;
|
||||
}
|
||||
|
||||
public static String getOsSkinFolder() {
|
||||
return getOsSdkFolder() + AndroidConstants.OS_SDK_SKINS_FOLDER;
|
||||
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER;
|
||||
}
|
||||
|
||||
public static synchronized boolean getAutoResRefresh() {
|
||||
@@ -540,18 +536,6 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
return sPlugin.mStore.getBoolean(PREFS_RES_AUTO_REFRESH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SDK build id.
|
||||
* @return a string containing the SDK build id, or null it it is unknownn.
|
||||
*/
|
||||
public static synchronized String getSdkApiVersion() {
|
||||
if (sPlugin != null) {
|
||||
return sPlugin.mSdkApiVersion;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static synchronized int getBuildVerbosity() {
|
||||
if (sPlugin != null) {
|
||||
return sPlugin.mBuildVerbosity;
|
||||
@@ -705,23 +689,25 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
final Display display = getDisplay();
|
||||
|
||||
// we need to ask the user what he wants to do.
|
||||
final BooleanWrapper wrapper = new BooleanWrapper();
|
||||
final boolean[] result = new boolean[1];
|
||||
display.syncExec(new Runnable() {
|
||||
public void run() {
|
||||
Shell shell = display.getActiveShell();
|
||||
wrapper.b = MessageDialog.openQuestion(shell, title, message);
|
||||
result[0] = MessageDialog.openQuestion(shell, title, message);
|
||||
}
|
||||
});
|
||||
return wrapper.b;
|
||||
return result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message to the default Eclipse log.
|
||||
*
|
||||
* @param severity The severity code. Valid values are: {@link IStatus#OK}, {@link IStatus#ERROR},
|
||||
* {@link IStatus#INFO}, {@link IStatus#WARNING} or {@link IStatus#CANCEL}.
|
||||
* @param severity The severity code. Valid values are: {@link IStatus#OK},
|
||||
* {@link IStatus#ERROR}, {@link IStatus#INFO}, {@link IStatus#WARNING} or
|
||||
* {@link IStatus#CANCEL}.
|
||||
* @param format The format string, like for {@link String#format(String, Object...)}.
|
||||
* @param args The arguments for the format string, like for {@link String#format(String, Object...)}.
|
||||
* @param args The arguments for the format string, like for
|
||||
* {@link String#format(String, Object...)}.
|
||||
*/
|
||||
public static void log(int severity, String format, Object ... args) {
|
||||
String message = String.format(format, args);
|
||||
@@ -845,7 +831,7 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
// now make sure it's not docked.
|
||||
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(
|
||||
CommonPlugin.getDefault().getAndroidConsole());
|
||||
AdtPlugin.getDefault().getAndroidConsole());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -883,21 +869,17 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link IJavaProject} to a list of projects to be recompiled once dx.jar is loaded.
|
||||
* @param javaProject
|
||||
* Returns whether the Sdk has been loaded. If the SDK has not been loaded, the given
|
||||
* <var>project</var> is added to a list of projects to recompile after the SDK is loaded.
|
||||
*/
|
||||
public void addPostDexProject(IJavaProject javaProject) {
|
||||
synchronized (mPostDexProjects) {
|
||||
if (DexWrapper.getStatus() == DexWrapper.LoadStatus.LOADED) {
|
||||
// Setup the new container for each project. By providing new instances of
|
||||
// AndroidClasspathContainer, this will force JDT to call
|
||||
// IClasspathContainer#getClasspathEntries() again and receive the new
|
||||
// path to the framework jar, and the project will be recompiled.
|
||||
AndroidClasspathContainerInitializer.updateProjects(new IJavaProject [] {
|
||||
javaProject });
|
||||
} else {
|
||||
mPostDexProjects.add(javaProject);
|
||||
public LoadStatus getSdkLoadStatus(IJavaProject project) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
// only add the project to the list, if we are still loading.
|
||||
if (mSdkIsLoaded == LoadStatus.LOADING && project != null) {
|
||||
mPostLoadProjects.add(project);
|
||||
}
|
||||
|
||||
return mSdkIsLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,9 +889,6 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
* @return false if the location is not correct.
|
||||
*/
|
||||
private boolean checkSdkLocationAndId() {
|
||||
// Reset the sdk build first in case the SDK is invalid and we abort.
|
||||
mSdkApiVersion = null;
|
||||
|
||||
if (mOsSdkLocation == null || mOsSdkLocation.length() == 0) {
|
||||
displayError(Messages.Dialog_Title_SDK_Location, Messages.SDK_Not_Setup);
|
||||
return false;
|
||||
@@ -949,17 +928,16 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
String.format(Messages.Could_Not_Find_Folder, osSdkLocation));
|
||||
}
|
||||
|
||||
String osTools = osSdkLocation + AndroidConstants.OS_SDK_TOOLS_FOLDER;
|
||||
String osTools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER;
|
||||
File toolsFolder = new File(osTools);
|
||||
if (toolsFolder.isDirectory() == false) {
|
||||
return errorHandler.handleError(
|
||||
String.format(Messages.Could_Not_Find_Folder_In_SDK,
|
||||
AndroidConstants.FD_TOOLS, osSdkLocation));
|
||||
SdkConstants.FD_TOOLS, osSdkLocation));
|
||||
}
|
||||
|
||||
// check the path to various tools we use
|
||||
String[] filesToCheck = new String[] {
|
||||
osSdkLocation + getOsRelativeFramework(),
|
||||
osSdkLocation + getOsRelativeAdb(),
|
||||
osSdkLocation + getOsRelativeAapt(),
|
||||
osSdkLocation + getOsRelativeAidl(),
|
||||
@@ -990,118 +968,106 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the usage start server.
|
||||
* Creates a job than can ping the usage server.
|
||||
*/
|
||||
private void pingUsageServer() {
|
||||
private Job createPingUsageServerJob() {
|
||||
// In order to not block the plugin loading, so we spawn another thread.
|
||||
new Thread("Ping!") { //$NON-NLS-1$
|
||||
Job job = new Job("Android SDK Ping") { // Job name, visible in progress view
|
||||
@Override
|
||||
public void run() {
|
||||
// get the version of the plugin
|
||||
String versionString = (String) getBundle().getHeaders().get(
|
||||
Constants.BUNDLE_VERSION);
|
||||
Version version = new Version(versionString);
|
||||
|
||||
SdkStatsHelper.pingUsageServer("adt", version); //$NON-NLS-1$
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Editors plugin.
|
||||
* <p/>
|
||||
* Since we do not want any dependencies between the plugins (Editors is an optional
|
||||
* plugin not needed for Android development), we attempt to start the plugin through
|
||||
* OSGi directly.
|
||||
* <p/>
|
||||
* This is done in another thread to not delay the start of this plugin.
|
||||
*/
|
||||
private void startEditorsPlugin() {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
// look for the bundle of the Editors plugin
|
||||
Bundle editorsBundle = Platform.getBundle(AndroidConstants.EDITORS_PLUGIN_ID);
|
||||
if (editorsBundle != null) {
|
||||
// we only start if the bundle is installed and not started.
|
||||
// STARTING means that its start is pending a triggering.
|
||||
int bundleState = editorsBundle.getState();
|
||||
if ((bundleState & (Bundle.RESOLVED | Bundle.INSTALLED
|
||||
| Bundle.STARTING)) != 0) {
|
||||
// Attempt to start it.
|
||||
// START_TRANSIENT is used because we don't want
|
||||
// to change the auto start value.
|
||||
editorsBundle.start(Bundle.START_TRANSIENT);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log(e, Messages.AdtPlugin_Failed_To_Start_s, AndroidConstants.EDITORS_PLUGIN_ID);
|
||||
|
||||
// get the version of the plugin
|
||||
String versionString = (String) getBundle().getHeaders().get(
|
||||
Constants.BUNDLE_VERSION);
|
||||
Version version = new Version(versionString);
|
||||
|
||||
SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$
|
||||
|
||||
return Status.OK_STATUS;
|
||||
} catch (Throwable t) {
|
||||
log(t, "pingUsageServer failed"); //$NON-NLS-1$
|
||||
return new Status(IStatus.ERROR, PLUGIN_ID,
|
||||
"pingUsageServer failed", t);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
};
|
||||
return job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SDK resources and set them in the {@link FrameworkResourceManager}.
|
||||
* Parses the SDK resources.
|
||||
*/
|
||||
private void parseSdkContent() {
|
||||
// Perform the update in a thread (here an Eclipse runtime job)
|
||||
// since this should never block the caller (especially the start method)
|
||||
new Job(Messages.AdtPlugin_Android_SDK_Content_Loader) {
|
||||
Job job = new Job(Messages.AdtPlugin_Android_SDK_Content_Loader) {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
SubMonitor progress = null;
|
||||
try {
|
||||
progress = SubMonitor.convert(monitor, Messages.AdtPlugin_Parsing_Resources, 100);
|
||||
|
||||
// load the values.
|
||||
FrameworkResourceParser parser = new FrameworkResourceParser();
|
||||
parser.parse(mOsSdkLocation, FrameworkResourceManager.getInstance(),
|
||||
progress);
|
||||
|
||||
// set the location of the layout lib jar file.
|
||||
FrameworkResourceManager.getInstance().setLayoutLibLocation(
|
||||
mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_LAYOUTLIB_JAR);
|
||||
FrameworkResourceManager.getInstance().setFrameworkResourcesLocation(
|
||||
mOsSdkLocation + AndroidConstants.OS_SDK_RESOURCES_FOLDER);
|
||||
FrameworkResourceManager.getInstance().setFrameworkFontLocation(
|
||||
mOsSdkLocation + AndroidConstants.OS_SDK_FONTS_FOLDER);
|
||||
} catch (Throwable e) {
|
||||
AdtPlugin.log(e, "Android SDK Resource Parser failed"); //$NON-NLS-1$
|
||||
AdtPlugin.printErrorToConsole(Messages.AdtPlugin_Android_SDK_Resource_Parser,
|
||||
Messages.AdtPlugin_Failed_To_Parse_s + e.getMessage());
|
||||
|
||||
return new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e);
|
||||
} finally {
|
||||
if (progress != null) {
|
||||
progress.worked(100);
|
||||
}
|
||||
}
|
||||
try {
|
||||
SubMonitor progress = SubMonitor.convert(monitor,
|
||||
"Initialize SDK Manager", 100);
|
||||
|
||||
try {
|
||||
progress = SubMonitor.convert(monitor, Messages.AdtPlugin_Parsing_Resources, 20);
|
||||
Sdk sdk = Sdk.loadSdk(mOsSdkLocation);
|
||||
|
||||
if (sdk != null) {
|
||||
|
||||
progress.setTaskName(Messages.AdtPlugin_Parsing_Resources);
|
||||
|
||||
for (IAndroidTarget target : sdk.getTargets()) {
|
||||
IStatus status = new AndroidTargetParser(target).run(progress);
|
||||
if (status.getCode() != IStatus.OK) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
mSdkIsLoaded = LoadStatus.FAILED;
|
||||
mPostLoadProjects.clear();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: move this per platform, or somewhere else.
|
||||
progress = SubMonitor.convert(monitor,
|
||||
Messages.AdtPlugin_Parsing_Resources, 20);
|
||||
DexWrapper.unloadDex();
|
||||
|
||||
IStatus res = DexWrapper.loadDex(
|
||||
mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_DX_JAR);
|
||||
if (res != Status.OK_STATUS) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
mSdkIsLoaded = LoadStatus.FAILED;
|
||||
mPostLoadProjects.clear();
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
}
|
||||
|
||||
synchronized (mPostLoadProjects) {
|
||||
mSdkIsLoaded = LoadStatus.LOADED;
|
||||
|
||||
// update the project that needs recompiling.
|
||||
synchronized (mPostDexProjects) {
|
||||
if (mPostDexProjects.size() > 0) {
|
||||
IJavaProject[] array = mPostDexProjects.toArray(
|
||||
new IJavaProject[mPostDexProjects.size()]);
|
||||
AndroidClasspathContainerInitializer.updateProjects(array);
|
||||
mPostDexProjects.clear();
|
||||
}
|
||||
if (mPostLoadProjects.size() > 0) {
|
||||
IJavaProject[] array = mPostLoadProjects.toArray(
|
||||
new IJavaProject[mPostLoadProjects.size()]);
|
||||
AndroidClasspathContainerInitializer.updateProjects(array);
|
||||
mPostLoadProjects.clear();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (progress != null) {
|
||||
progress.worked(20);
|
||||
}
|
||||
|
||||
// Notify resource changed listeners
|
||||
progress.subTask("Refresh UI");
|
||||
progress.setWorkRemaining(mResourceRefreshListener.size());
|
||||
|
||||
// Clone the list before iterating, to avoid Concurrent Modification
|
||||
// exceptions
|
||||
List<Runnable> listeners = (List<Runnable>)mResourceRefreshListener.clone();
|
||||
for (Runnable listener : listeners) {
|
||||
try {
|
||||
AdtPlugin.getDisplay().syncExec(listener);
|
||||
} catch (Exception e) {
|
||||
AdtPlugin.log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$
|
||||
} finally {
|
||||
progress.worked(1);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -1112,6 +1078,252 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
};
|
||||
job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs
|
||||
job.schedule();
|
||||
}
|
||||
|
||||
/** Returns the global android console */
|
||||
public MessageConsole getAndroidConsole() {
|
||||
return mAndroidConsole;
|
||||
}
|
||||
|
||||
// ----- Methods for Editors -------
|
||||
|
||||
public void startEditors() {
|
||||
sAndroidLogoDesc = imageDescriptorFromPlugin(AdtPlugin.PLUGIN_ID,
|
||||
"/icons/android.png"); //$NON-NLS-1$
|
||||
sAndroidLogo = sAndroidLogoDesc.createImage();
|
||||
|
||||
// get the stream to write in the android console.
|
||||
MessageConsole androidConsole = AdtPlugin.getDefault().getAndroidConsole();
|
||||
mAndroidConsoleStream = androidConsole.newMessageStream();
|
||||
|
||||
mAndroidConsoleErrorStream = androidConsole.newMessageStream();
|
||||
mRed = new Color(getDisplay(), 0xFF, 0x00, 0x00);
|
||||
|
||||
// because this can be run, in some cases, by a non ui thread, and beccause
|
||||
// changing the console properties update the ui, we need to make this change
|
||||
// in the ui thread.
|
||||
getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
mAndroidConsoleErrorStream.setColor(mRed);
|
||||
}
|
||||
});
|
||||
|
||||
// Add a resource listener to handle compiled resources.
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
mResourceMonitor = ResourceMonitor.startMonitoring(ws);
|
||||
|
||||
if (mResourceMonitor != null) {
|
||||
try {
|
||||
setupDefaultEditor(mResourceMonitor);
|
||||
ResourceManager.setup(mResourceMonitor);
|
||||
} catch (Throwable t) {
|
||||
log(t, "ResourceManager.setup failed"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
|
||||
* method saves this plug-in's preference and dialog stores and shuts down
|
||||
* its image registry (if they are in use). Subclasses may extend this
|
||||
* method, but must send super <b>last</b>. A try-finally statement should
|
||||
* be used where necessary to ensure that <code>super.shutdown()</code> is
|
||||
* always done.
|
||||
*
|
||||
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void stopEditors() {
|
||||
sAndroidLogo.dispose();
|
||||
|
||||
IconFactory.getInstance().Dispose();
|
||||
|
||||
// Remove the resource listener that handles compiled resources.
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
ResourceMonitor.stopMonitoring(ws);
|
||||
|
||||
mRed.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Image for the small Android logo.
|
||||
*
|
||||
* Callers should not dispose it.
|
||||
*/
|
||||
public static Image getAndroidLogo() {
|
||||
return sAndroidLogo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ImageDescriptor} for the small Android logo.
|
||||
*
|
||||
* Callers should not dispose it.
|
||||
*/
|
||||
public static ImageDescriptor getAndroidLogoDesc() {
|
||||
return sAndroidLogoDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ResourceMonitor object.
|
||||
*/
|
||||
public ResourceMonitor getResourceMonitor() {
|
||||
return mResourceMonitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the editor to register default editors for resource files when needed.
|
||||
*
|
||||
* This is called by the {@link AdtPlugin} during initialization.
|
||||
*
|
||||
* @param monitor The main Resource Monitor object.
|
||||
*/
|
||||
public void setupDefaultEditor(ResourceMonitor monitor) {
|
||||
monitor.addFileListener(new IFileListener() {
|
||||
|
||||
private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$
|
||||
|
||||
/* (non-Javadoc)
|
||||
* Sent when a file changed.
|
||||
* @param file The file that changed.
|
||||
* @param markerDeltas The marker deltas for the file.
|
||||
* @param kind The change kind. This is equivalent to
|
||||
* {@link IResourceDelta#accept(IResourceDeltaVisitor)}
|
||||
*
|
||||
* @see IFileListener#fileChanged
|
||||
*/
|
||||
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
|
||||
if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
|
||||
// The resources files must have a file path similar to
|
||||
// project/res/.../*.xml
|
||||
// There is no support for sub folders, so the segment count must be 4
|
||||
if (file.getFullPath().segmentCount() == 4) {
|
||||
// check if we are inside the res folder.
|
||||
String segment = file.getFullPath().segment(1);
|
||||
if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) {
|
||||
// we are inside a res/ folder, get the actual ResourceFolder
|
||||
ProjectResources resources = ResourceManager.getInstance().
|
||||
getProjectResources(file.getProject());
|
||||
|
||||
// This happens when importing old Android projects in Eclipse
|
||||
// that lack the container (probably because resources fail to build
|
||||
// properly.)
|
||||
if (resources == null) {
|
||||
log(IStatus.INFO,
|
||||
"getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$
|
||||
file.getFullPath().toOSString(),
|
||||
file.getProject().getName());
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceFolder resFolder = resources.getResourceFolder(
|
||||
(IFolder)file.getParent());
|
||||
|
||||
if (resFolder != null) {
|
||||
if (kind == IResourceDelta.ADDED) {
|
||||
resourceAdded(file, resFolder.getType());
|
||||
} else if (kind == IResourceDelta.CHANGED) {
|
||||
resourceChanged(file, resFolder.getType());
|
||||
}
|
||||
} else {
|
||||
// if the res folder is null, this means the name is invalid,
|
||||
// in this case we remove whatever android editors that was set
|
||||
// as the default editor.
|
||||
IEditorDescriptor desc = IDE.getDefaultEditor(file);
|
||||
String editorId = desc.getId();
|
||||
if (editorId.startsWith(AndroidConstants.EDITORS_NAMESPACE)) {
|
||||
// reset the default editor.
|
||||
IDE.setDefaultEditor(file, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceAdded(IFile file, ResourceFolderType type) {
|
||||
// set the default editor based on the type.
|
||||
if (type == ResourceFolderType.LAYOUT) {
|
||||
IDE.setDefaultEditor(file, LayoutEditor.ID);
|
||||
} else if (type == ResourceFolderType.DRAWABLE
|
||||
|| type == ResourceFolderType.VALUES) {
|
||||
IDE.setDefaultEditor(file, ResourcesEditor.ID);
|
||||
} else if (type == ResourceFolderType.MENU) {
|
||||
IDE.setDefaultEditor(file, MenuEditor.ID);
|
||||
} else if (type == ResourceFolderType.XML) {
|
||||
if (XmlEditor.canHandleFile(file)) {
|
||||
IDE.setDefaultEditor(file, XmlEditor.ID);
|
||||
} else {
|
||||
// set a property to determine later if the XML can be handled
|
||||
QualifiedName qname = new QualifiedName(
|
||||
AdtPlugin.PLUGIN_ID,
|
||||
UNKNOWN_EDITOR);
|
||||
try {
|
||||
file.setPersistentProperty(qname, "1");
|
||||
} catch (CoreException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceChanged(IFile file, ResourceFolderType type) {
|
||||
if (type == ResourceFolderType.XML) {
|
||||
IEditorDescriptor ed = IDE.getDefaultEditor(file);
|
||||
if (ed == null || ed.getId() != XmlEditor.ID) {
|
||||
QualifiedName qname = new QualifiedName(
|
||||
AdtPlugin.PLUGIN_ID,
|
||||
UNKNOWN_EDITOR);
|
||||
String prop = null;
|
||||
try {
|
||||
prop = file.getPersistentProperty(qname);
|
||||
} catch (CoreException e) {
|
||||
// pass
|
||||
}
|
||||
if (prop != null && XmlEditor.canHandleFile(file)) {
|
||||
try {
|
||||
// remove the property & set editor
|
||||
file.setPersistentProperty(qname, null);
|
||||
IWorkbenchPage page = PlatformUI.getWorkbench().
|
||||
getActiveWorkbenchWindow().getActivePage();
|
||||
|
||||
IEditorPart oldEditor = page.findEditor(new FileEditorInput(file));
|
||||
if (oldEditor != null &&
|
||||
AdtPlugin.displayPrompt("Android XML Editor",
|
||||
String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?",
|
||||
file.getFullPath()))) {
|
||||
IDE.setDefaultEditor(file, XmlEditor.ID);
|
||||
IEditorPart newEditor = page.openEditor(
|
||||
new FileEditorInput(file),
|
||||
XmlEditor.ID,
|
||||
true, /* activate */
|
||||
IWorkbenchPage.MATCH_NONE);
|
||||
|
||||
if (newEditor != null) {
|
||||
page.closeEditor(oldEditor, true /* save */);
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// setPersistentProperty or page.openEditor may have failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, IResourceDelta.ADDED | IResourceDelta.CHANGED);
|
||||
}
|
||||
|
||||
public void addResourceChangedListener(Runnable resourceRefreshListener) {
|
||||
mResourceRefreshListener.add(resourceRefreshListener);
|
||||
}
|
||||
|
||||
public void removeResourceChangedListener(Runnable resourceRefreshListener) {
|
||||
mResourceRefreshListener.remove(resourceRefreshListener);
|
||||
}
|
||||
|
||||
public static synchronized OutputStream getErrorStream() {
|
||||
return sPlugin.mAndroidConsoleErrorStream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package com.android.ide.eclipse.adt;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.framework.Version;
|
||||
@@ -42,19 +41,6 @@ import java.util.regex.Pattern;
|
||||
*
|
||||
*/
|
||||
final class VersionCheck {
|
||||
|
||||
/** Pattern to get the SDK build incremental version from the
|
||||
* <code>$SDK/tools/lib/build.prop file</code>. */
|
||||
private final static Pattern sBuildVersionPattern = Pattern.compile(
|
||||
"^" + Device.PROP_BUILD_VERSION + "=(.+)$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Pattern to parse release type SDK version number. This parses the content read with
|
||||
* <code>sBuildIdPattern</code>.
|
||||
*/
|
||||
private final static Pattern sSdkVersionPattern = Pattern.compile(
|
||||
"^(\\d+)\\.(\\d+)_r(\\d+)$", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Pattern to get the minimum plugin version supported by the SDK. This is read from
|
||||
* the file <code>$SDK/tools/lib/plugin.prop</code>.
|
||||
@@ -69,58 +55,7 @@ final class VersionCheck {
|
||||
*/
|
||||
public static boolean checkVersion(String osSdkPath, CheckSdkErrorHandler errorHandler) {
|
||||
AdtPlugin plugin = AdtPlugin.getDefault();
|
||||
String osLibs = osSdkPath + AndroidConstants.OS_SDK_LIBS_FOLDER;
|
||||
|
||||
/*
|
||||
* All plugins should work with all SDKs. Newer SDKs may require a newer plugin
|
||||
* but this is handled below.
|
||||
* Still, we need to grab the SDK version from this file. This is used
|
||||
* to compare to running emulator/device when launching run/debug sessions.
|
||||
*/
|
||||
try {
|
||||
FileReader reader = new FileReader(osLibs + AndroidConstants.FN_BUILD_PROP);
|
||||
BufferedReader bReader = new BufferedReader(reader);
|
||||
String line;
|
||||
while ((line = bReader.readLine()) != null) {
|
||||
Matcher m = sBuildVersionPattern.matcher(line);
|
||||
if (m.matches()) {
|
||||
plugin.mSdkApiVersion = m.group(1).trim();
|
||||
|
||||
/*
|
||||
* No checks on the version at the moment.
|
||||
*/
|
||||
/*
|
||||
if (plugin.mSdkBuildVersion != null) {
|
||||
// attempt to get version number from the build id
|
||||
m = sSdkVersionPattern.matcher(plugin.mSdkBuildVersion);
|
||||
if (m.matches()) {
|
||||
// get the platform version number
|
||||
int platformMajor = Integer.parseInt(m.group(1));
|
||||
int platformMinor = Integer.parseInt(m.group(2));
|
||||
@SuppressWarnings("unused") //$NON-NLS-1$
|
||||
int sdkRelease = Integer.parseInt(m.group(3));
|
||||
|
||||
if (platformMajor != 0 || platformMinor != 9) {
|
||||
return errorHandler.handleError(String.format(
|
||||
"This version of ADT requires the Android SDK version 0.9\n\nCurrent version is %1$s.\n\nPlease update your SDK to the latest version.",
|
||||
plugin.mSdkBuildVersion));
|
||||
}
|
||||
} else {
|
||||
// unknown version format.
|
||||
AdtPlugin.printErrorToConsole(
|
||||
(Object)String.format(Messages.VersionCheck_Unable_To_Parse_Version_s,
|
||||
plugin.mSdkBuildVersion));
|
||||
}
|
||||
}
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// the build id will be null, and this is handled by the builders.
|
||||
} catch (IOException e) {
|
||||
// the build id will be null, and this is handled by the builders.
|
||||
}
|
||||
String osLibs = osSdkPath + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
|
||||
|
||||
// get the plugin property file, and grab the minimum plugin version required
|
||||
// to work with the sdk
|
||||
@@ -128,7 +63,7 @@ final class VersionCheck {
|
||||
int minMinorVersion = -1;
|
||||
int minMicroVersion = -1;
|
||||
try {
|
||||
FileReader reader = new FileReader(osLibs + AndroidConstants.FN_PLUGIN_PROP);
|
||||
FileReader reader = new FileReader(osLibs + SdkConstants.FN_PLUGIN_PROP);
|
||||
BufferedReader bReader = new BufferedReader(reader);
|
||||
String line;
|
||||
while ((line = bReader.readLine()) != null) {
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.ide.eclipse.adt.build;
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.jarutils.DebugKeyProvider;
|
||||
@@ -28,6 +30,7 @@ import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
|
||||
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
||||
import com.android.jarutils.SignedJarBuilder.IZipEntryFilter;
|
||||
import com.android.prefs.AndroidLocation.AndroidLocationException;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
@@ -44,6 +47,7 @@ import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
@@ -296,6 +300,15 @@ public class ApkBuilder extends BaseBuilder {
|
||||
saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources);
|
||||
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
|
||||
|
||||
// At this point, we can abort the build if we have to, as we have computed
|
||||
// our resource delta and stored the result.
|
||||
|
||||
// check if we have finished loading the SDK.
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
|
||||
// we exit silently
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// Now check the compiler compliance level, not displaying the error
|
||||
// message since this is not the first builder.
|
||||
if (ProjectHelper.checkCompilerCompliance(getProject())
|
||||
@@ -502,7 +515,8 @@ public class ApkBuilder extends BaseBuilder {
|
||||
commandArray.add(osAssetsPath);
|
||||
}
|
||||
commandArray.add("-I"); //$NON-NLS-1$
|
||||
commandArray.add(AdtPlugin.getOsAbsoluteFramework());
|
||||
commandArray.add(
|
||||
Sdk.getCurrent().getTarget(project).getPath(IAndroidTarget.ANDROID_JAR));
|
||||
commandArray.add("-F"); //$NON-NLS-1$
|
||||
commandArray.add(osOutFilePath);
|
||||
|
||||
@@ -584,16 +598,9 @@ public class ApkBuilder extends BaseBuilder {
|
||||
DexWrapper wrapper = DexWrapper.getWrapper();
|
||||
|
||||
if (wrapper == null) {
|
||||
if (DexWrapper.getStatus() == DexWrapper.LoadStatus.FAILED) {
|
||||
if (DexWrapper.getStatus() == LoadStatus.FAILED) {
|
||||
throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
|
||||
Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
|
||||
} else {
|
||||
// means we haven't loaded the dex jar yet.
|
||||
// We set the project to be recompiled after dex is loaded.
|
||||
AdtPlugin.getDefault().addPostDexProject(javaProject);
|
||||
|
||||
// and we exit silently
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +684,7 @@ public class ApkBuilder extends BaseBuilder {
|
||||
}
|
||||
|
||||
// TODO: get the store type from somewhere else.
|
||||
DebugKeyProvider provider = new DebugKeyProvider(null /* storeType */,
|
||||
DebugKeyProvider provider = new DebugKeyProvider(osKeyPath, null /* storeType */,
|
||||
new IKeyGenOutput() {
|
||||
public void err(String message) {
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(),
|
||||
@@ -747,9 +754,18 @@ public class ApkBuilder extends BaseBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// now write the native libraries.
|
||||
// First look if the lib folder is there.
|
||||
IResource libFolder = javaProject.getProject().findMember(
|
||||
AndroidConstants.FD_NATIVE_LIBS);
|
||||
if (libFolder != null && libFolder.exists() &&
|
||||
libFolder.getType() == IResource.FOLDER) {
|
||||
// look inside and put .so in lib/* by keeping the relative folder path.
|
||||
writeNativeLibraries(libFolder.getFullPath().segmentCount(), builder, libFolder);
|
||||
}
|
||||
|
||||
// close the jar file and write the manifest and sign it.
|
||||
builder.close();
|
||||
|
||||
} catch (GeneralSecurityException e1) {
|
||||
// mark project and return
|
||||
String msg = String.format(Messages.Final_Archive_Error_s, e1.getMessage());
|
||||
@@ -784,6 +800,12 @@ public class ApkBuilder extends BaseBuilder {
|
||||
|
||||
// and also output it in the console
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
} catch (CoreException e) {
|
||||
// mark project and return
|
||||
String msg = String.format(Messages.Final_Archive_Error_s, e.getMessage());
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
@@ -797,6 +819,48 @@ public class ApkBuilder extends BaseBuilder {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes native libraries into a {@link SignedJarBuilder}.
|
||||
* <p/>This recursively go through folder and writes .so files.
|
||||
* The path in the archive is based on the root folder containing the libraries in the project.
|
||||
* Its segment count is passed to the method to compute the resources path relative to the root
|
||||
* folder.
|
||||
* Native libraries in the archive must be in a "lib" folder. Everything in the project native
|
||||
* lib folder directly goes in this "lib" folder in the archive.
|
||||
*
|
||||
*
|
||||
* @param rooSegmentCount The number of segment of the path of the folder containing the
|
||||
* libraries. This is used to compute the path in the archive.
|
||||
* @param jarBuilder the {@link SignedJarBuilder} used to create the archive.
|
||||
* @param resource the IResource to write.
|
||||
* @throws CoreException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeNativeLibraries(int rootSegmentCount, SignedJarBuilder jarBuilder,
|
||||
IResource resource) throws CoreException, IOException {
|
||||
if (resource.getType() == IResource.FILE) {
|
||||
IPath path = resource.getFullPath();
|
||||
|
||||
// check the extension.
|
||||
if (path.getFileExtension().equalsIgnoreCase(AndroidConstants.EXT_NATIVE_LIB)) {
|
||||
// remove the first segment to build the path inside the archive.
|
||||
path = path.removeFirstSegments(rootSegmentCount);
|
||||
|
||||
// add it to the archive.
|
||||
IPath apkPath = new Path(AndroidConstants.FD_APK_NATIVE_LIBS);
|
||||
apkPath = apkPath.append(path);
|
||||
|
||||
// writes the file in the apk.
|
||||
jarBuilder.writeFile(resource.getLocation().toFile(), apkPath.toString());
|
||||
}
|
||||
} else if (resource.getType() == IResource.FOLDER) {
|
||||
IResource[] members = ((IFolder)resource).members();
|
||||
for (IResource member : members) {
|
||||
writeNativeLibraries(rootSegmentCount, jarBuilder, member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the standard resources of a project and its referenced projects
|
||||
* into a {@link SignedJarBuilder}.
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.ArrayList;
|
||||
* <li>Any change to the classes.dex inside the output folder</li>
|
||||
* <li>Any change to the packaged resources file inside the output folder</li>
|
||||
* <li>Any change to a non java/aidl file inside the source folders</li>
|
||||
* <li>Any change to .so file inside the lib (native library) folder</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
@@ -79,6 +80,8 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
|
||||
private IPath mResPath;
|
||||
|
||||
private IPath mLibFolder;
|
||||
|
||||
/**
|
||||
* Builds the object with a specified output folder.
|
||||
* @param builder the xml builder using this object to visit the
|
||||
@@ -104,6 +107,11 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
if (resFolder != null) {
|
||||
mResPath = resFolder.getFullPath();
|
||||
}
|
||||
|
||||
IResource libFolder = builder.getProject().findMember(AndroidConstants.FD_NATIVE_LIBS);
|
||||
if (libFolder != null) {
|
||||
mLibFolder = libFolder.getFullPath();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getConvertToDex() {
|
||||
@@ -161,6 +169,7 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
|
||||
// check the other folders.
|
||||
if (mOutputPath != null && mOutputPath.isPrefixOf(path)) {
|
||||
// a resource changed inside the output folder.
|
||||
if (type == IResource.FILE) {
|
||||
// just check this is a .class file. Any modification will
|
||||
// trigger a change in the classes.dex file
|
||||
@@ -217,6 +226,17 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
mPackageResources = true;
|
||||
mMakeFinalPackage = true;
|
||||
return false;
|
||||
} else if (mLibFolder != null && mLibFolder.isPrefixOf(path)) {
|
||||
// inside the native library folder. Test if the changed resource is a .so file.
|
||||
if (type == IResource.FILE &&
|
||||
path.getFileExtension().equalsIgnoreCase(AndroidConstants.EXT_NATIVE_LIB)) {
|
||||
mMakeFinalPackage = true;
|
||||
return false; // return false for file.
|
||||
}
|
||||
|
||||
// for folders, return true only if we don't already know we have to make the
|
||||
// final package.
|
||||
return mMakeFinalPackage == false;
|
||||
} else {
|
||||
// we are in a folder that is neither the resource folders, nor the output.
|
||||
// check against all the source folders, unless we already know we need to do
|
||||
|
||||
@@ -806,12 +806,6 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
|
||||
// get the IPath
|
||||
IPath path = e.getPath();
|
||||
|
||||
// get the file name. if it's the framework jar, we ignore that file.
|
||||
// since we now use classpath container, this is here for legacy purpose only.
|
||||
if (AndroidConstants.FN_FRAMEWORK_LIBRARY.equals(path.lastSegment())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check the name ends with .jar
|
||||
if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
|
||||
boolean local = false;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.ide.eclipse.adt.build;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
@@ -47,9 +48,6 @@ public final class DexWrapper {
|
||||
|
||||
private static DexWrapper sWrapper;
|
||||
|
||||
/** Status for the Loading of the dex jar file */
|
||||
public enum LoadStatus { LOADING, LOADED, FAILED }
|
||||
|
||||
private static LoadStatus sLoadStatus = LoadStatus.LOADING;
|
||||
|
||||
private Method mRunMethod;
|
||||
|
||||
@@ -18,14 +18,16 @@ package com.android.ide.eclipse.adt.build;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor;
|
||||
import com.android.ide.eclipse.adt.project.FixLaunchConfig;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.XmlErrorHandler.BasicXmlErrorListener;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
@@ -44,7 +46,6 @@ import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.ui.ide.IDE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -130,7 +131,6 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
mSource);
|
||||
try {
|
||||
mNewFile.setDerived(true);
|
||||
IDE.setDefaultEditor(mNewFile, ReadOnlyJavaEditor.ID);
|
||||
} catch (CoreException e) {
|
||||
// This really shouldn't happen since we check that the resource exist.
|
||||
// Worst case scenario, the resource isn't marked as derived.
|
||||
@@ -266,8 +266,14 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
|
||||
// record the state
|
||||
mCompileResources |= dv.getCompileResources();
|
||||
mergeAidlFileModifications(dv.getAidlToCompile(),
|
||||
dv.getAidlToRemove());
|
||||
|
||||
// handle aidl modification
|
||||
if (dv.getFullAidlRecompilation()) {
|
||||
buildAidlCompilationList(project, sourceList);
|
||||
} else {
|
||||
mergeAidlFileModifications(dv.getAidlToCompile(),
|
||||
dv.getAidlToRemove());
|
||||
}
|
||||
|
||||
// if there was some XML errors, we just return w/o doing
|
||||
// anything since we've put some markers in the files anyway.
|
||||
@@ -290,6 +296,12 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
// At this point we have stored what needs to be build, so we can
|
||||
// do some high level test and abort if needed.
|
||||
|
||||
// check if we have finished loading the SDK.
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
|
||||
// we exit silently
|
||||
return null;
|
||||
}
|
||||
|
||||
// check the compiler compliance level, not displaying the error message
|
||||
// since this is not the first builder.
|
||||
if (ProjectHelper.checkCompilerCompliance(getProject())
|
||||
@@ -302,13 +314,19 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
// Check that the SDK directory has been setup.
|
||||
String osSdkFolder = AdtPlugin.getOsSdkFolder();
|
||||
|
||||
if (osSdkFolder.length() == 0) {
|
||||
if (osSdkFolder == null || osSdkFolder.length() == 0) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.No_SDK_Setup_Error);
|
||||
markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
return null;
|
||||
}
|
||||
|
||||
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
|
||||
if (projectTarget == null) {
|
||||
// no target. error has been output by the container initializer: exit silently.
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the manifest file
|
||||
IFile manifest = AndroidManifestHelper.getManifest(project);
|
||||
@@ -448,7 +466,7 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
array.add("-S"); //$NON-NLS-1$
|
||||
array.add(osResPath);
|
||||
array.add("-I"); //$NON-NLS-1$
|
||||
array.add(AdtPlugin.getOsAbsoluteFramework());
|
||||
array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
|
||||
if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -563,6 +581,8 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS,
|
||||
mManifestPackageSourceFolder, mManifestPackage);
|
||||
}
|
||||
|
||||
// FIXME: delete all java generated from aidl.
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -736,12 +756,14 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
if (mAidlToCompile.size() == 0 && mAidlToRemove.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// create the command line
|
||||
String[] command = new String[4 + sourceFolders.size() + (folderAidlPath != null ? 1 : 0)];
|
||||
int index = 0;
|
||||
int aidlIndex;
|
||||
command[index++] = AdtPlugin.getOsAbsoluteAidl();
|
||||
command[index++] = "-p" + AdtPlugin.getOsAbsoluteFrameworkAidl(); //$NON-NLS-1$
|
||||
command[aidlIndex = index++] = "-p"; //$NON-NLS-1$
|
||||
if (folderAidlPath != null) {
|
||||
command[index++] = "-p" + folderAidlPath; //$NON-NLS-1$
|
||||
}
|
||||
@@ -813,6 +835,8 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
prepareFileForExternalModification(javaFile);
|
||||
|
||||
// finish to set the command line.
|
||||
command[aidlIndex] = "-p" + Sdk.getCurrent().getTarget(aidlFile.getProject()).getPath(
|
||||
IAndroidTarget.ANDROID_AIDL); //$NON-NLS-1$
|
||||
command[index] = osPath;
|
||||
command[index + 1] = osJavaPath;
|
||||
|
||||
@@ -922,7 +946,8 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through the buildpath and fills the list of aidl files to compile.
|
||||
* Goes through the build paths and fills the list of aidl files to compile
|
||||
* ({@link #mAidlToCompile}).
|
||||
* @param project The project.
|
||||
* @param buildPaths The list of build paths.
|
||||
*/
|
||||
@@ -977,7 +1002,7 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
scanContainerForAidl((IFolder)r);
|
||||
break;
|
||||
default:
|
||||
// this would mean it's a project or the workspaceroot
|
||||
// this would mean it's a project or the workspace root
|
||||
// which is unlikely to happen. we do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.ArrayList;
|
||||
class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
IResourceDeltaVisitor {
|
||||
|
||||
// Result fields.
|
||||
/**
|
||||
* Compile flag. This is set to true if one of the changed/added/removed
|
||||
* file is a resource file. Upon visiting all the delta resources, if
|
||||
@@ -50,6 +51,22 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
*/
|
||||
private boolean mCompileResources = false;
|
||||
|
||||
/** List of .aidl files found that are modified or new. */
|
||||
private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>();
|
||||
|
||||
/** List of .aidl files that have been removed. */
|
||||
private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>();
|
||||
|
||||
/** Aidl forced recompilation flag. This is set to true if project.aidl is modified. */
|
||||
private boolean mFullAidlCompilation = false;
|
||||
|
||||
/** Manifest check/parsing flag. */
|
||||
private boolean mCheckedManifestXml = false;
|
||||
|
||||
/** Application Pacakge, gathered from the parsing of the manifest */
|
||||
private String mJavaPackage = null;
|
||||
|
||||
// Internal usage fields.
|
||||
/**
|
||||
* In Resource folder flag. This allows us to know if we're in the
|
||||
* resource folder.
|
||||
@@ -65,14 +82,6 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
/** List of source folders. */
|
||||
private ArrayList<IPath> mSourceFolders;
|
||||
|
||||
/** List of .aidl files found that are modified or new. */
|
||||
private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>();
|
||||
|
||||
/** List of .aidl files that have been removed. */
|
||||
private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>();
|
||||
|
||||
private boolean mCheckedManifestXml = false;
|
||||
private String mJavaPackage = null;
|
||||
|
||||
public PreCompilerDeltaVisitor(BaseBuilder builder, ArrayList<IPath> sourceFolders) {
|
||||
super(builder);
|
||||
@@ -90,6 +99,10 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
public ArrayList<IFile> getAidlToRemove() {
|
||||
return mAidlToRemove;
|
||||
}
|
||||
|
||||
public boolean getFullAidlRecompilation() {
|
||||
return mFullAidlCompilation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the manifest file was parsed/checked for error during the resource delta
|
||||
@@ -100,7 +113,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the manifest package if the manifest was checked.
|
||||
* Returns the manifest package if the manifest was checked/parsed.
|
||||
* <p/>
|
||||
* This can return null in two cases:
|
||||
* <ul>
|
||||
@@ -169,11 +182,14 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
// we don't want to go to the children, not like they are
|
||||
// any for this resource anyway.
|
||||
return false;
|
||||
} else if (AndroidConstants.FN_PROJECT_AIDL.equalsIgnoreCase(segments[1])) {
|
||||
// need to force recompilation of all the aidl files
|
||||
mFullAidlCompilation = true;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we can either be in the source folder or in the
|
||||
// resource foler or in a different folder that contains a source
|
||||
// resource folder or in a different folder that contains a source
|
||||
// folder.
|
||||
// This is due to not all source folder being src/. Some could be
|
||||
// something/somethingelse/src/
|
||||
@@ -227,7 +243,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, true);
|
||||
}
|
||||
|
||||
// we add it anyway so that we can try to compile it every time.
|
||||
// we add it anyway so that we can try to compile it at every compilation
|
||||
// until the conflict is fixed.
|
||||
mAidlToCompile.add(file);
|
||||
|
||||
} else {
|
||||
@@ -245,6 +262,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
|
||||
boolean outputMessage = false;
|
||||
|
||||
// Special case of R.java/Manifest.java.
|
||||
// FIXME: This does not check the package. Any modification of R.java/Manifest.java in another project will trigger a new recompilation of the resources.
|
||||
if (AndroidConstants.FN_RESOURCE_CLASS.equals(fileName) ||
|
||||
AndroidConstants.FN_MANIFEST_CLASS.equals(fileName)) {
|
||||
// if it was removed, there's a possibility that it was removed due to a
|
||||
|
||||
@@ -62,13 +62,10 @@ public class ResourceManagerBuilder extends IncrementalProjectBuilder {
|
||||
switch (res) {
|
||||
case ProjectHelper.COMPILER_COMPLIANCE_LEVEL:
|
||||
errorMessage = Messages.Requires_Compiler_Compliance_5;
|
||||
return null;
|
||||
case ProjectHelper.COMPILER_COMPLIANCE_SOURCE:
|
||||
errorMessage = Messages.Requires_Source_Compatibility_5;
|
||||
return null;
|
||||
case ProjectHelper.COMPILER_COMPLIANCE_CODEGEN_TARGET:
|
||||
errorMessage = Messages.Requires_Class_Compatibility_5;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (errorMessage != null) {
|
||||
|
||||
@@ -30,9 +30,9 @@ import com.android.ddmlib.SyncService.SyncResult;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.DeviceChooserDialog.DeviceChooserResponse;
|
||||
import com.android.ide.eclipse.adt.debug.ui.EmulatorConfigTab;
|
||||
import com.android.ide.eclipse.adt.debug.ui.SkinRepository;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
@@ -102,6 +102,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
/** Debuggable attribute of the manifest file. */
|
||||
Boolean mDebuggable = null;
|
||||
|
||||
/** Required ApiVersionNumber by the app. 0 means no requirements */
|
||||
int mRequiredApiVersionNumber = 0;
|
||||
|
||||
InstallRetryMode mRetryMode = InstallRetryMode.NEVER;
|
||||
|
||||
/**
|
||||
@@ -126,8 +129,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
/** Basic constructor with activity and package info. */
|
||||
public DelayedLaunchInfo(IProject project, String packageName, String activity,
|
||||
IFile pack, Boolean debuggable, int launchAction, AndroidLaunch launch,
|
||||
IProgressMonitor monitor) {
|
||||
IFile pack, Boolean debuggable, int requiredApiVersionNumber, int launchAction,
|
||||
AndroidLaunch launch, IProgressMonitor monitor) {
|
||||
mProject = project;
|
||||
mPackageName = packageName;
|
||||
mActivity = activity;
|
||||
@@ -136,6 +139,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
mLaunch = launch;
|
||||
mMonitor = monitor;
|
||||
mDebuggable = debuggable;
|
||||
mRequiredApiVersionNumber = requiredApiVersionNumber;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,13 +260,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
try {
|
||||
mSkin = config.getAttribute(LaunchConfigDelegate.ATTR_SKIN, mSkin);
|
||||
if (mSkin == null) {
|
||||
mSkin = SkinRepository.getInstance().checkSkin(
|
||||
LaunchConfigDelegate.DEFAULT_SKIN);
|
||||
} else {
|
||||
mSkin = SkinRepository.getInstance().checkSkin(mSkin);
|
||||
mSkin = SdkConstants.SKIN_DEFAULT;
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
mSkin = SkinRepository.getInstance().checkSkin(LaunchConfigDelegate.DEFAULT_SKIN);
|
||||
mSkin = SdkConstants.SKIN_DEFAULT;
|
||||
}
|
||||
|
||||
int index = LaunchConfigDelegate.DEFAULT_SPEED;
|
||||
@@ -539,7 +540,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
LaunchConfigDelegate.DEFAULT_DELAY);
|
||||
|
||||
// default skin
|
||||
wc.setAttribute(LaunchConfigDelegate.ATTR_SKIN, LaunchConfigDelegate.DEFAULT_SKIN);
|
||||
wc.setAttribute(LaunchConfigDelegate.ATTR_SKIN, SdkConstants.SKIN_DEFAULT);
|
||||
|
||||
// default wipe data mode
|
||||
wc.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA,
|
||||
@@ -599,13 +600,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
* defined by <code>ILaunchManager</code> - <code>RUN_MODE</code> or
|
||||
* <code>DEBUG_MODE</code>.
|
||||
* @param apk the resource to the apk to launch.
|
||||
* @param debuggable
|
||||
* @param debuggable the debuggable value of the app, or null if not set.
|
||||
* @param requiredApiVersionNumber the api version required by the app, or -1 if none.
|
||||
* @param activity the class to provide to am to launch
|
||||
* @param config the launch configuration
|
||||
* @param launch the launch object
|
||||
*/
|
||||
public void launch(final IProject project, String mode, IFile apk,
|
||||
String packageName, Boolean debuggable, String activity,
|
||||
String packageName, Boolean debuggable, int requiredApiVersionNumber, String activity,
|
||||
final AndroidLaunchConfiguration config, final AndroidLaunch launch,
|
||||
IProgressMonitor monitor) {
|
||||
|
||||
@@ -619,7 +621,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
// create the launch info
|
||||
final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName,
|
||||
activity, apk, debuggable, config.mLaunchAction, launch, monitor);
|
||||
activity, apk, debuggable, requiredApiVersionNumber, config.mLaunchAction,
|
||||
launch, monitor);
|
||||
|
||||
// set the debug mode
|
||||
launchInfo.mDebugMode = mode.equals(ILaunchManager.DEBUG_MODE);
|
||||
@@ -711,6 +714,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
// stop the launch and return
|
||||
mWaitingForEmulatorLaunches.remove(launchInfo);
|
||||
AdtPlugin.printErrorToConsole(project, "Launch canceled!");
|
||||
launch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
@@ -761,31 +765,63 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) {
|
||||
/**
|
||||
* Checks the build information, and returns whether the launch should continue.
|
||||
* <p/>The value tested are:
|
||||
* <ul>
|
||||
* <li>Minimum API version requested by the application. If the target device does not match,
|
||||
* the launch is canceled.</li>
|
||||
* <li>Debuggable attribute of the application and whether or not the device requires it. If
|
||||
* the device requires it and it is not set in the manifest, the launch will be forced to
|
||||
* "release" mode instead of "debug"</li>
|
||||
* <ul>
|
||||
* @param launchInfo
|
||||
* @param device
|
||||
* @return
|
||||
*/
|
||||
private boolean checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) {
|
||||
if (device != null) {
|
||||
// get the SDK build
|
||||
String sdkBuild = AdtPlugin.getSdkApiVersion();
|
||||
|
||||
// can only complain if the sdkBuild is known
|
||||
if (sdkBuild != null) {
|
||||
|
||||
String deviceVersion = device.getProperty(Device.PROP_BUILD_VERSION);
|
||||
|
||||
if (deviceVersion == null) {
|
||||
AdtPlugin.printToConsole(launchInfo.mProject, "WARNING: Unknown device API version!");
|
||||
} else {
|
||||
if (sdkBuild.equals(deviceVersion) == false) {
|
||||
// TODO do a proper check, including testing the content of the uses-sdk string in the manifest to detect real incompatibility.
|
||||
String msg = String.format(
|
||||
"WARNING: Device API version (%1$s) does not match SDK API version (%2$s)",
|
||||
deviceVersion, sdkBuild);
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject, msg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AdtPlugin.printToConsole(launchInfo.mProject, "WARNING: Unknown SDK API version!");
|
||||
// check the app required API level versus the target device API level
|
||||
|
||||
String deviceApiVersionName = device.getProperty(Device.PROP_BUILD_VERSION);
|
||||
String value = device.getProperty(Device.PROP_BUILD_VERSION_NUMBER);
|
||||
int deviceApiVersionNumber = 0;
|
||||
try {
|
||||
deviceApiVersionNumber = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
// pass, we'll keep the deviceVersionNumber value at 0.
|
||||
}
|
||||
|
||||
if (launchInfo.mRequiredApiVersionNumber == 0) {
|
||||
// warn the API level requirement is not set.
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject,
|
||||
"WARNING: Application does not specify an API level requirement!");
|
||||
|
||||
// and display the target device API level (if known)
|
||||
if (deviceApiVersionName == null || deviceApiVersionNumber == 0) {
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject,
|
||||
"WARNING: Unknown device API version!");
|
||||
} else {
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format(
|
||||
"Device API version is %1$d (Android %2$s)", deviceApiVersionNumber,
|
||||
deviceApiVersionName));
|
||||
}
|
||||
} else { // app requires a specific API level
|
||||
if (deviceApiVersionName == null || deviceApiVersionNumber == 0) {
|
||||
AdtPlugin.printToConsole(launchInfo.mProject,
|
||||
"WARNING: Unknown device API version!");
|
||||
} else if (deviceApiVersionNumber < launchInfo.mRequiredApiVersionNumber) {
|
||||
String msg = String.format(
|
||||
"ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).",
|
||||
launchInfo.mRequiredApiVersionNumber, deviceApiVersionNumber,
|
||||
deviceApiVersionName);
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject, msg);
|
||||
|
||||
// abort the launch
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// now checks that the device/app can be debugged (if needed)
|
||||
if (device.isEmulator() == false && launchInfo.mDebugMode) {
|
||||
String debuggableDevice = device.getProperty(Device.PROP_DEBUGGABLE);
|
||||
@@ -818,6 +854,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -829,10 +867,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
* @return true if succeed
|
||||
*/
|
||||
private boolean simpleLaunch(DelayedLaunchInfo launchInfo, Device device) {
|
||||
checkBuildInfo(launchInfo, device);
|
||||
// API level check
|
||||
if (checkBuildInfo(launchInfo, device) == false) {
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!");
|
||||
launchInfo.mLaunch.stopLaunch();
|
||||
return false;
|
||||
}
|
||||
|
||||
// sync the app
|
||||
if (syncApp(launchInfo, device) == false) {
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!");
|
||||
launchInfo.mLaunch.stopLaunch();
|
||||
return false;
|
||||
}
|
||||
@@ -1127,6 +1171,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
} catch (IOException e) {
|
||||
// something went wrong trying to launch the app.
|
||||
// lets stop the Launch
|
||||
AdtPlugin.printErrorToConsole(info.mProject,
|
||||
String.format("Launch error: %s", e.getMessage()));
|
||||
info.mLaunch.stopLaunch();
|
||||
|
||||
// and remove it from the list of app waiting for debuggers
|
||||
@@ -1139,25 +1185,20 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
private boolean launchEmulator(AndroidLaunchConfiguration config) {
|
||||
|
||||
// split the custom command line in segments
|
||||
String[] segs;
|
||||
ArrayList<String> customArgs = new ArrayList<String>();
|
||||
boolean has_wipe_data = false;
|
||||
if (config.mEmulatorCommandLine != null && config.mEmulatorCommandLine.length() > 0) {
|
||||
segs = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$
|
||||
String[] segments = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$
|
||||
|
||||
// we need to remove the empty strings
|
||||
ArrayList<String> array = new ArrayList<String>();
|
||||
for (String s : segs) {
|
||||
for (String s : segments) {
|
||||
if (s.length() > 0) {
|
||||
array.add(s);
|
||||
customArgs.add(s);
|
||||
if (!has_wipe_data && s.equals(FLAG_WIPE_DATA)) {
|
||||
has_wipe_data = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
segs = array.toArray(new String[array.size()]);
|
||||
} else {
|
||||
segs = new String[0];
|
||||
}
|
||||
|
||||
boolean needs_wipe_data = config.mWipeData && !has_wipe_data;
|
||||
@@ -1167,30 +1208,38 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
boolean needs_no_boot_anim = config.mNoBootAnim;
|
||||
// build the command line based on the available parameters.
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
list.add(AdtPlugin.getOsAbsoluteEmulator());
|
||||
if (config.mSkin != null) {
|
||||
list.add(FLAG_SKIN);
|
||||
list.add(config.mSkin);
|
||||
}
|
||||
|
||||
if (config.mNetworkSpeed != null) {
|
||||
list.add(FLAG_NETSPEED);
|
||||
list.add(config.mNetworkSpeed);
|
||||
}
|
||||
|
||||
if (config.mNetworkDelay != null) {
|
||||
list.add(FLAG_NETDELAY);
|
||||
list.add(config.mNetworkDelay);
|
||||
}
|
||||
|
||||
// get the command line
|
||||
String[] command = new String[7 + segs.length +
|
||||
(needs_wipe_data ? 1 : 0) +
|
||||
(needs_no_boot_anim ? 1 : 0)];
|
||||
int index = 0;
|
||||
command[index++] = AdtPlugin.getOsAbsoluteEmulator();
|
||||
command[index++] = FLAG_SKIN; //$NON-NLS-1$
|
||||
command[index++] = config.mSkin;
|
||||
command[index++] = FLAG_NETSPEED; //$NON-NLS-1$
|
||||
command[index++] = config.mNetworkSpeed;
|
||||
command[index++] = FLAG_NETDELAY; //$NON-NLS-1$
|
||||
command[index++] = config.mNetworkDelay;
|
||||
if (needs_wipe_data) {
|
||||
command[index++] = FLAG_WIPE_DATA;
|
||||
list.add(FLAG_WIPE_DATA);
|
||||
}
|
||||
if (needs_no_boot_anim) {
|
||||
command[index++] = FLAG_NO_BOOT_ANIM;
|
||||
}
|
||||
for (String s : segs) {
|
||||
command[index++] = s;
|
||||
|
||||
if (config.mNoBootAnim) {
|
||||
list.add(FLAG_NO_BOOT_ANIM);
|
||||
}
|
||||
|
||||
list.addAll(customArgs);
|
||||
|
||||
// convert the list into an array for the call to exec.
|
||||
String[] command = list.toArray(new String[list.size()]);
|
||||
|
||||
// launch the emulator
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
@@ -1278,12 +1327,11 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
/**
|
||||
* Launch a new thread that connects a remote debugger on the specified port.
|
||||
* @param debugPort The port to connect the debugger to
|
||||
* @param launch The associated AndroidLaunch object.
|
||||
* @param androidLaunch The associated AndroidLaunch object.
|
||||
* @param monitor A Progress monitor
|
||||
* @see connectRemoveDebugger()
|
||||
*/
|
||||
public static void launchRemoteDebugger(final ILaunchConfiguration config,
|
||||
final int debugPort, final AndroidLaunch androidLaunch,
|
||||
public static void launchRemoteDebugger( final int debugPort, final AndroidLaunch androidLaunch,
|
||||
final IProgressMonitor monitor) {
|
||||
new Thread("Debugger connection") { //$NON-NLS-1$
|
||||
@Override
|
||||
@@ -1342,13 +1390,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
synchronized (sListLock) {
|
||||
// look if there's an app waiting for a device
|
||||
if (mWaitingForEmulatorLaunches.size() > 0) {
|
||||
// remove first item from the list
|
||||
// get/remove first launch item from the list
|
||||
// FIXME: what if we have multiple launches waiting?
|
||||
DelayedLaunchInfo launchInfo = mWaitingForEmulatorLaunches.get(0);
|
||||
mWaitingForEmulatorLaunches.remove(0);
|
||||
|
||||
// give it its device
|
||||
|
||||
// give the launch item its device for later use.
|
||||
launchInfo.mDevice = device;
|
||||
|
||||
|
||||
// and move it to the other list
|
||||
mWaitingForReadyEmulatorList.add(launchInfo);
|
||||
|
||||
@@ -1433,12 +1482,23 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
// look for application waiting for home
|
||||
synchronized (sListLock) {
|
||||
boolean foundMatch = false;
|
||||
for (int i = 0 ; i < mWaitingForReadyEmulatorList.size() ;) {
|
||||
DelayedLaunchInfo launchInfo = mWaitingForReadyEmulatorList.get(i);
|
||||
if (launchInfo.mDevice == device) {
|
||||
// it's match, remove from the list
|
||||
mWaitingForReadyEmulatorList.remove(i);
|
||||
|
||||
// We couldn't check earlier the API level of the device
|
||||
// (it's asynchronous when the device boot, and usually
|
||||
// deviceConnected is called before it's queried for its build info)
|
||||
// so we check now
|
||||
if (checkBuildInfo(launchInfo, device) == false) {
|
||||
// device is not the proper API!
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject,
|
||||
"Launch canceled!");
|
||||
launchInfo.mLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
AdtPlugin.printToConsole(launchInfo.mProject,
|
||||
String.format("HOME is up on device '%1$s'",
|
||||
@@ -1448,16 +1508,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
if (syncApp(launchInfo, device)) {
|
||||
// application package is sync'ed, lets attempt to launch it.
|
||||
launchApp(launchInfo, device);
|
||||
|
||||
// if we haven't checked the device build info, lets do it here
|
||||
if (foundMatch == false) {
|
||||
foundMatch = true;
|
||||
checkBuildInfo(launchInfo, device);
|
||||
}
|
||||
} else {
|
||||
// failure! Cancel and return
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject,
|
||||
"Launch canceled!");
|
||||
launchInfo.mLaunch.stopLaunch();
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
@@ -1530,6 +1588,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// well something went wrong.
|
||||
AdtPlugin.printErrorToConsole(launchInfo.mProject,
|
||||
String.format("Launch error: %s", e.getMessage()));
|
||||
// stop the launch
|
||||
launchInfo.mLaunch.stopLaunch();
|
||||
}
|
||||
|
||||
@@ -83,11 +83,6 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
|
||||
/** Skin to be used to launch the emulator */
|
||||
public static final String ATTR_SKIN = AdtPlugin.PLUGIN_ID + ".skin"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Name of the default Skin.
|
||||
*/
|
||||
public static final String DEFAULT_SKIN = "HVGA"; //$NON-NLS-1$
|
||||
|
||||
public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
@@ -138,8 +133,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
|
||||
// if we have a valid debug port, this means we're debugging an app
|
||||
// that's already launched.
|
||||
if (debugPort != INVALID_DEBUG_PORT) {
|
||||
AndroidLaunchController.launchRemoteDebugger(configuration,
|
||||
debugPort, androidLaunch, monitor);
|
||||
AndroidLaunchController.launchRemoteDebugger(debugPort, androidLaunch, monitor);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -302,7 +296,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
|
||||
// everything seems fine, we ask the launch controller to handle
|
||||
// the rest
|
||||
controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
|
||||
manifestParser.getDebuggable(), activityName, config, androidLaunch, monitor);
|
||||
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
|
||||
activityName, config, androidLaunch, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,13 +18,19 @@ package com.android.ide.eclipse.adt.debug.ui;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate;
|
||||
import com.android.ide.eclipse.adt.debug.ui.SkinRepository.Skin;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
|
||||
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
@@ -82,6 +88,8 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
|
||||
private Button mNoBootAnimButton;
|
||||
|
||||
private IAndroidTarget mTarget;
|
||||
|
||||
/**
|
||||
* Returns the emulator ready speed option value.
|
||||
* @param value The index of the combo selection.
|
||||
@@ -169,12 +177,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Screen Size:");
|
||||
|
||||
mSkinCombo = new Combo(mEmulatorOptionsGroup, SWT.READ_ONLY);
|
||||
Skin[] skins = SkinRepository.getInstance().getSkins();
|
||||
if (skins != null) {
|
||||
for (Skin skin : skins) {
|
||||
mSkinCombo.add(skin.getDescription());
|
||||
}
|
||||
}
|
||||
mSkinCombo.addSelectionListener(new SelectionAdapter() {
|
||||
// called when selection changes
|
||||
@Override
|
||||
@@ -182,7 +184,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
mSkinCombo.pack();
|
||||
|
||||
// network options
|
||||
new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Network Speed:");
|
||||
@@ -287,6 +288,42 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
}
|
||||
mAutoTargetButton.setSelection(value);
|
||||
mManualTargetButton.setSelection(!value);
|
||||
|
||||
// look for the project name to get its target.
|
||||
String projectName = "";
|
||||
try {
|
||||
projectName = configuration.getAttribute(
|
||||
IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName);
|
||||
} catch (CoreException ce) {
|
||||
}
|
||||
|
||||
IProject project = null;
|
||||
|
||||
// get the list of existing Android projects from the workspace.
|
||||
IJavaProject[] projects = BaseProjectHelper.getAndroidProjects();
|
||||
if (projects != null) {
|
||||
// look for the project whose name we read from the configuration.
|
||||
for (IJavaProject p : projects) {
|
||||
if (p.getElementName().equals(projectName)) {
|
||||
project = p.getProject();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSkinCombo.removeAll();
|
||||
if (project != null) {
|
||||
mTarget = Sdk.getCurrent().getTarget(project);
|
||||
if (mTarget != null) {
|
||||
String[] skins = mTarget.getSkins();
|
||||
if (skins != null) {
|
||||
for (String skin : skins) {
|
||||
mSkinCombo.add(skin);
|
||||
}
|
||||
mSkinCombo.pack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
|
||||
try {
|
||||
@@ -307,16 +344,18 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
int index = -1;
|
||||
try {
|
||||
String skin = configuration.getAttribute(LaunchConfigDelegate.ATTR_SKIN, (String)null);
|
||||
if (skin != null) {
|
||||
index = getSkinIndex(skin);
|
||||
if (skin == null) {
|
||||
skin = SdkConstants.SKIN_DEFAULT;
|
||||
}
|
||||
|
||||
index = getSkinIndex(skin);
|
||||
} catch (CoreException e) {
|
||||
index = getSkinIndex(SkinRepository.getInstance().checkSkin(
|
||||
LaunchConfigDelegate.DEFAULT_SKIN));
|
||||
index = getSkinIndex(SdkConstants.SKIN_DEFAULT);
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
mSkinCombo.clearSelection();
|
||||
mSkinCombo.select(0);
|
||||
updateLaunchConfigurationDialog();
|
||||
} else {
|
||||
mSkinCombo.select(index);
|
||||
}
|
||||
@@ -387,7 +426,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
|
||||
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN,
|
||||
LaunchConfigDelegate.DEFAULT_SKIN);
|
||||
SdkConstants.SKIN_DEFAULT);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
|
||||
LaunchConfigDelegate.DEFAULT_SPEED);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
|
||||
@@ -403,11 +442,31 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
}
|
||||
|
||||
private String getSkinNameByIndex(int index) {
|
||||
return SkinRepository.getInstance().getSkinNameByIndex(index);
|
||||
if (mTarget != null && index > 0) {
|
||||
String[] skins = mTarget.getSkins();
|
||||
if (skins != null && index < skins.length) {
|
||||
return skins[index];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getSkinIndex(String name) {
|
||||
return SkinRepository.getInstance().getSkinIndex(name);
|
||||
if (mTarget != null) {
|
||||
String[] skins = mTarget.getSkins();
|
||||
if (skins != null) {
|
||||
int index = 0;
|
||||
for (String skin : skins) {
|
||||
if (skin.equalsIgnoreCase(name)) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* 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.debug.ui;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Repository for the emulator skins. This class is responsible for parsing the skin folder.
|
||||
*/
|
||||
public class SkinRepository {
|
||||
|
||||
private final static SkinRepository sInstance = new SkinRepository();
|
||||
|
||||
private Skin[] mSkins;
|
||||
|
||||
public static class Skin {
|
||||
|
||||
String mName;
|
||||
|
||||
public Skin(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human readable description of the skin.
|
||||
*/
|
||||
public String getDescription() {
|
||||
// TODO: parse the skin and output the description.
|
||||
return mName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
*/
|
||||
public static SkinRepository getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the skin folder and build the skin list.
|
||||
* @param osPath The path of the skin folder.
|
||||
*/
|
||||
public void parseFolder(String osPath) {
|
||||
File skinFolder = new File(osPath);
|
||||
|
||||
if (skinFolder.isDirectory()) {
|
||||
ArrayList<Skin> skinList = new ArrayList<Skin>();
|
||||
|
||||
File[] files = skinFolder.listFiles();
|
||||
|
||||
for (File skin : files) {
|
||||
if (skin.isDirectory()) {
|
||||
// check for layout file
|
||||
File layout = new File(skin.getPath() + File.separator
|
||||
+ AndroidConstants.FN_LAYOUT);
|
||||
|
||||
if (layout.isFile()) {
|
||||
// for now we don't parse the content of the layout and
|
||||
// simply add the directory to the list.
|
||||
skinList.add(new Skin(skin.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSkins = skinList.toArray(new Skin[skinList.size()]);
|
||||
} else {
|
||||
mSkins = new Skin[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Skin[] getSkins() {
|
||||
return mSkins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid skin folder name. If <code>skin</code> is valid, then it is returned,
|
||||
* otherwise the first valid skin name is returned.
|
||||
* @param skin the Skin name to check
|
||||
* @return a valid skin name or null if there aren't any.
|
||||
*/
|
||||
public String checkSkin(String skin) {
|
||||
if (mSkins != null) {
|
||||
for (Skin s : mSkins) {
|
||||
if (s.getName().equals(skin)) {
|
||||
return skin;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSkins.length > 0) {
|
||||
return mSkins[0].getName();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of a skin by index.
|
||||
* @param index The index of the skin to return
|
||||
* @return the skin name of null if the index is invalid.
|
||||
*/
|
||||
public String getSkinNameByIndex(int index) {
|
||||
if (mSkins != null) {
|
||||
if (index >= 0 && index < mSkins.length) {
|
||||
return mSkins[index].getName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index (0 based) of the skin matching the name.
|
||||
* @param name The name of the skin to look for.
|
||||
* @return the index of the skin or -1 if the skin was not found.
|
||||
*/
|
||||
public int getSkinIndex(String name) {
|
||||
if (mSkins != null) {
|
||||
int count = mSkins.length;
|
||||
for (int i = 0 ; i < count ; i++) {
|
||||
Skin s = mSkins[i];
|
||||
if (s.mName.equals(name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.editors.java;
|
||||
|
||||
import org.eclipse.jdt.ui.PreferenceConstants;
|
||||
import org.eclipse.jdt.ui.text.IJavaPartitions;
|
||||
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
|
||||
import org.eclipse.jdt.ui.text.JavaTextTools;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
|
||||
|
||||
/**
|
||||
* Read only java editors. This looks like the regular Java editor, except that it
|
||||
* prevents editing the files. This is used for automatically generated java classes.
|
||||
*/
|
||||
public class ReadOnlyJavaEditor extends AbstractDecoratedTextEditor {
|
||||
|
||||
public final static String ID =
|
||||
"com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor"; //$NON-NLS-1$
|
||||
|
||||
public ReadOnlyJavaEditor() {
|
||||
IPreferenceStore javaUiStore = PreferenceConstants.getPreferenceStore();
|
||||
JavaTextTools jtt = new JavaTextTools(javaUiStore);
|
||||
|
||||
JavaSourceViewerConfiguration jsvc = new JavaSourceViewerConfiguration(
|
||||
jtt.getColorManager(), javaUiStore, this, IJavaPartitions.JAVA_PARTITIONING);
|
||||
|
||||
setSourceViewerConfiguration(jsvc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@ package com.android.ide.eclipse.adt.preferences;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.jarutils.DebugKeyProvider;
|
||||
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
||||
import com.android.prefs.AndroidLocation.AndroidLocationException;
|
||||
|
||||
import org.eclipse.jface.preference.BooleanFieldEditor;
|
||||
import org.eclipse.jface.preference.FieldEditorPreferencePage;
|
||||
@@ -29,6 +32,13 @@ import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPreferencePage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Preference page for build options.
|
||||
*
|
||||
@@ -75,7 +85,7 @@ public class BuildPreferencePage extends FieldEditorPreferencePage implements
|
||||
addField(new ReadOnlyFieldEditor(AdtPlugin.PREFS_DEFAULT_DEBUG_KEYSTORE,
|
||||
Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent()));
|
||||
|
||||
addField(new FileFieldEditor(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE,
|
||||
addField(new KeystoreFieldEditor(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE,
|
||||
Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent()));
|
||||
|
||||
}
|
||||
@@ -105,4 +115,103 @@ public class BuildPreferencePage extends FieldEditorPreferencePage implements
|
||||
control.setEditable(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link FileFieldEditor} that checks that the keystore is valid.
|
||||
*/
|
||||
private static class KeystoreFieldEditor extends FileFieldEditor {
|
||||
public KeystoreFieldEditor(String name, String label, Composite parent) {
|
||||
super(name, label, parent);
|
||||
setValidateStrategy(VALIDATE_ON_KEY_STROKE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkState() {
|
||||
String fileName = getTextControl().getText();
|
||||
fileName = fileName.trim();
|
||||
|
||||
// empty values are considered ok.
|
||||
if (fileName.length() > 0) {
|
||||
File file = new File(fileName);
|
||||
if (file.isFile()) {
|
||||
// attempt to load the debug key.
|
||||
try {
|
||||
DebugKeyProvider provider = new DebugKeyProvider(fileName,
|
||||
null /* storeType */, null /* key gen output */);
|
||||
PrivateKey key = provider.getDebugKey();
|
||||
X509Certificate certificate = (X509Certificate)provider.getCertificate();
|
||||
|
||||
if (key == null || certificate == null) {
|
||||
showErrorMessage("Unable to find debug key in keystore!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Date today = new Date();
|
||||
if (certificate.getNotAfter().compareTo(today) < 0) {
|
||||
showErrorMessage("Certificate is expired!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (certificate.getNotBefore().compareTo(today) > 0) {
|
||||
showErrorMessage("Certificate validity is in the future!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're good!
|
||||
clearErrorMessage();
|
||||
return true;
|
||||
} catch (GeneralSecurityException e) {
|
||||
handleException(e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
return false;
|
||||
} catch (KeytoolException e) {
|
||||
handleException(e);
|
||||
return false;
|
||||
} catch (AndroidLocationException e) {
|
||||
handleException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// file does not exist.
|
||||
showErrorMessage("Not a valid keystore path.");
|
||||
return false; // Apply/OK must be disabled
|
||||
}
|
||||
}
|
||||
|
||||
clearErrorMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getTextControl(Composite parent) {
|
||||
setValidateStrategy(VALIDATE_ON_KEY_STROKE);
|
||||
return super.getTextControl(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error message from a {@link Throwable}. If the exception has no message, try
|
||||
* to get the message from the cause.
|
||||
* @param t the Throwable.
|
||||
*/
|
||||
private void handleException(Throwable t) {
|
||||
String msg = t.getMessage();
|
||||
if (t == null) {
|
||||
Throwable cause = t.getCause();
|
||||
if (cause != null) {
|
||||
handleException(cause);
|
||||
} else {
|
||||
setErrorMessage("Uknown error when getting the debug key!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// valid text, display it.
|
||||
showErrorMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.ide.eclipse.adt.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
@@ -117,7 +118,7 @@ public class CreateAidlImportAction implements IObjectActionDelegate {
|
||||
// create the file with the parcelables
|
||||
if (parcelables.size() > 0) {
|
||||
IPath path = project.getLocation();
|
||||
path = path.append("/project.aidl"); //$NON-NLS-1$
|
||||
path = path.append(AndroidConstants.FN_PROJECT_AIDL);
|
||||
|
||||
File f = new File(path.toOSString());
|
||||
if (f.exists() == false) {
|
||||
@@ -194,7 +195,7 @@ public class CreateAidlImportAction implements IObjectActionDelegate {
|
||||
|
||||
IType[] superInterfaces = typeHierarchy.getAllSuperInterfaces(type);
|
||||
for (IType superInterface : superInterfaces) {
|
||||
if ("android.os.Parcelable".equals(superInterface.getFullyQualifiedName())) { //$NON-NLS-1$
|
||||
if (AndroidConstants.CLASS_PARCELABLE.equals(superInterface.getFullyQualifiedName())) {
|
||||
parcelables.add(type.getFullyQualifiedName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.jface.viewers.IDecoration;
|
||||
import org.eclipse.jface.viewers.ILabelDecorator;
|
||||
import org.eclipse.jface.viewers.ILabelProviderListener;
|
||||
import org.eclipse.jface.viewers.ILightweightLabelDecorator;
|
||||
|
||||
/**
|
||||
* A {@link ILabelDecorator} associated with an org.eclipse.ui.decorators extension.
|
||||
* This is used to add android icons in some special folders in the package explorer.
|
||||
*/
|
||||
public class FolderDecorator implements ILightweightLabelDecorator {
|
||||
|
||||
private ImageDescriptor mDescriptor;
|
||||
|
||||
public FolderDecorator() {
|
||||
mDescriptor = AdtPlugin.getImageDescriptor("/icons/android_project.png");
|
||||
}
|
||||
|
||||
public void decorate(Object element, IDecoration decoration) {
|
||||
if (element instanceof IFolder) {
|
||||
IFolder folder = (IFolder)element;
|
||||
|
||||
// get the project and make sure this is an android project
|
||||
IProject project = folder.getProject();
|
||||
|
||||
try {
|
||||
if (project.hasNature(AndroidConstants.NATURE)) {
|
||||
// check the folder is directly under the project.
|
||||
if (folder.getParent().getType() == IResource.PROJECT) {
|
||||
String name = folder.getName();
|
||||
if (name.equals(AndroidConstants.FD_ASSETS)) {
|
||||
decorate(decoration, " [Android assets]");
|
||||
decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT);
|
||||
} else if (name.equals(AndroidConstants.FD_RESOURCES)) {
|
||||
decorate(decoration, " [Android resources]");
|
||||
decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT);
|
||||
} else if (name.equals(AndroidConstants.FD_NATIVE_LIBS)) {
|
||||
decorate(decoration, " [Native Libraries]");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// log the error
|
||||
AdtPlugin.log(e, "Unable to get nature of project '%s'.", project.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void decorate(IDecoration decoration, String suffix) {
|
||||
decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT);
|
||||
|
||||
// this is broken as it changes the color of the whole text, not only of the decoration.
|
||||
// TODO: figure out how to change the color of the decoration only.
|
||||
// decoration.addSuffix(suffix);
|
||||
// ITheme theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
|
||||
// ColorRegistry registry = theme.getColorRegistry();
|
||||
// decoration.setForegroundColor(registry.get("org.eclipse.jdt.ui.ColoredLabels.decorations"));
|
||||
|
||||
}
|
||||
|
||||
public boolean isLabelProperty(Object element, String property) {
|
||||
// at this time return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addListener(ILabelProviderListener listener) {
|
||||
// No state change will affect the rendering.
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void removeListener(ILabelProviderListener listener) {
|
||||
// No state change will affect the rendering.
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// nothind to dispose
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
|
||||
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.wizard.WizardDialog;
|
||||
import org.eclipse.ui.IObjectActionDelegate;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
|
||||
public class NewXmlFileWizardAction implements IObjectActionDelegate {
|
||||
|
||||
private ISelection mSelection;
|
||||
private IWorkbench mWorkbench;
|
||||
|
||||
/**
|
||||
* @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
|
||||
*/
|
||||
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
||||
mWorkbench = targetPart.getSite().getWorkbenchWindow().getWorkbench();
|
||||
}
|
||||
|
||||
public void run(IAction action) {
|
||||
if (mSelection instanceof IStructuredSelection) {
|
||||
IStructuredSelection selection = (IStructuredSelection)mSelection;
|
||||
|
||||
// call the new xml file wizard on the current selection.
|
||||
NewXmlFileWizard wizard = new NewXmlFileWizard();
|
||||
wizard.init(mWorkbench, selection);
|
||||
WizardDialog dialog = new WizardDialog(mWorkbench.getDisplay().getActiveShell(),
|
||||
wizard);
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
this.mSelection = selection;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.QualifiedName;
|
||||
import org.eclipse.jdt.core.IClasspathAttribute;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
@@ -177,31 +176,6 @@ public final class ProjectHelper {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of the javadoc attributes in a classpath entry.
|
||||
* @param frameworkEntry the classpath entry to check.
|
||||
* @return true if the javadoc attributes is valid, false otherwise.
|
||||
*/
|
||||
public static boolean checkJavaDocPath(IClasspathEntry frameworkEntry) {
|
||||
// get the list of extra attributes
|
||||
IClasspathAttribute[] attributes = frameworkEntry.getExtraAttributes();
|
||||
|
||||
// and search for the one about the javadoc
|
||||
for (IClasspathAttribute att : attributes) {
|
||||
if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME.
|
||||
equals(att.getName())) {
|
||||
// we found a javadoc attribute. Now we test its value.
|
||||
// get the expect value
|
||||
String validJavaDoc = getJavaDocPath(AdtPlugin.getOsAbsoluteFramework());
|
||||
|
||||
// now compare and return
|
||||
return validJavaDoc.equals(att.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the project. This checks the SDK location.
|
||||
* @param project The project to fix.
|
||||
@@ -264,14 +238,7 @@ public final class ProjectHelper {
|
||||
continue;
|
||||
}
|
||||
} else if (kind == IClasspathEntry.CPE_CONTAINER) {
|
||||
IPath containerPath = entry.getPath();
|
||||
|
||||
if (AndroidClasspathContainerInitializer.checkOldPath(containerPath)) {
|
||||
entries = ProjectHelper.removeEntryFromClasspath(entries, i);
|
||||
|
||||
// continue, to skip the i++;
|
||||
continue;
|
||||
} else if (AndroidClasspathContainerInitializer.checkPath(containerPath)) {
|
||||
if (AndroidClasspathContainerInitializer.checkPath(entry.getPath())) {
|
||||
foundContainer = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ package com.android.ide.eclipse.adt.project.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.jarutils.KeystoreHelper;
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
|
||||
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
@@ -27,28 +30,38 @@ import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.wizard.Wizard;
|
||||
import org.eclipse.jface.wizard.WizardPage;
|
||||
import org.eclipse.swt.events.VerifyEvent;
|
||||
import org.eclipse.swt.events.VerifyListener;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IExportWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.KeyStore.PrivateKeyEntry;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Export wizard to export an apk signed with a release key/certificate.
|
||||
*/
|
||||
public class ExportWizard extends Wizard implements IExportWizard {
|
||||
public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
|
||||
private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
|
||||
|
||||
private static final String PAGE_PRE = "preExportPage"; //$NON-NLS-1$
|
||||
private static final String PAGE_SIGNING = "signingExportPage"; //$NON-NLS-1$
|
||||
private static final String PAGE_FINAL = "finalExportPage"; //$NON-NLS-1$
|
||||
private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$
|
||||
private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$
|
||||
private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$
|
||||
private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$
|
||||
private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$
|
||||
|
||||
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
|
||||
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
|
||||
@@ -59,7 +72,41 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
*/
|
||||
static abstract class ExportWizardPage extends WizardPage {
|
||||
|
||||
protected boolean mNewProjectReference = true;
|
||||
/** bit mask constant for project data change event */
|
||||
protected static final int DATA_PROJECT = 0x001;
|
||||
/** bit mask constant for keystore data change event */
|
||||
protected static final int DATA_KEYSTORE = 0x002;
|
||||
/** bit mask constant for key data change event */
|
||||
protected static final int DATA_KEY = 0x004;
|
||||
|
||||
protected static final VerifyListener sPasswordVerifier = new VerifyListener() {
|
||||
public void verifyText(VerifyEvent e) {
|
||||
// verify the characters are valid for password.
|
||||
int len = e.text.length();
|
||||
|
||||
// first limit to 127 characters max
|
||||
if (len + ((Text)e.getSource()).getText().length() > 127) {
|
||||
e.doit = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// now only take non control characters
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
if (e.text.charAt(i) < 32) {
|
||||
e.doit = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bit mask indicating what changed while the page was hidden.
|
||||
* @see #DATA_PROJECT
|
||||
* @see #DATA_KEYSTORE
|
||||
* @see #DATA_KEY
|
||||
*/
|
||||
protected int mProjectDataChanged = 0;
|
||||
|
||||
ExportWizardPage(String name) {
|
||||
super(name);
|
||||
@@ -72,23 +119,38 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
super.setVisible(visible);
|
||||
if (visible) {
|
||||
onShow();
|
||||
mNewProjectReference = false;
|
||||
mProjectDataChanged = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void newProjectReference() {
|
||||
mNewProjectReference = true;
|
||||
final void projectDataChanged(int changeMask) {
|
||||
mProjectDataChanged |= changeMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
|
||||
* {@link Throwable} object.
|
||||
*/
|
||||
protected final void onException(Throwable t) {
|
||||
String message = getExceptionMessage(t);
|
||||
|
||||
setErrorMessage(message);
|
||||
setPageComplete(false);
|
||||
}
|
||||
}
|
||||
|
||||
private ExportWizardPage mPages[] = new ExportWizardPage[3];
|
||||
private ExportWizardPage mPages[] = new ExportWizardPage[5];
|
||||
|
||||
private IProject mProject;
|
||||
|
||||
private String mKeystore;
|
||||
private String mKeystorePassword;
|
||||
private boolean mKeystoreCreationMode;
|
||||
|
||||
private String mKeyAlias;
|
||||
private char[] mKeystorePassword;
|
||||
private char[] mKeyPassword;
|
||||
private String mKeyPassword;
|
||||
private int mValidity;
|
||||
private String mDName;
|
||||
|
||||
private PrivateKey mPrivateKey;
|
||||
private X509Certificate mCertificate;
|
||||
@@ -97,6 +159,15 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
private String mApkFilePath;
|
||||
private String mApkFileName;
|
||||
|
||||
private ExportWizardPage mKeystoreSelectionPage;
|
||||
private ExportWizardPage mKeyCreationPage;
|
||||
private ExportWizardPage mKeySelectionPage;
|
||||
private ExportWizardPage mKeyCheckPage;
|
||||
|
||||
private boolean mKeyCreationMode;
|
||||
|
||||
private List<String> mExistingAliases;
|
||||
|
||||
public ExportWizard() {
|
||||
setHelpAvailable(false); // TODO have help
|
||||
setWindowTitle("Export Android Application");
|
||||
@@ -105,46 +176,101 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
|
||||
@Override
|
||||
public void addPages() {
|
||||
addPage(mPages[0] = new PreExportPage(this, PAGE_PRE));
|
||||
addPage(mPages[1] = new SigningExportPage(this, PAGE_SIGNING));
|
||||
addPage(mPages[2] = new FinalExportPage(this, PAGE_FINAL));
|
||||
addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK));
|
||||
addPage(mKeystoreSelectionPage = mPages[1] = new KeystoreSelectionPage(this,
|
||||
PAGE_KEYSTORE_SELECTION));
|
||||
addPage(mKeyCreationPage = mPages[2] = new KeyCreationPage(this, PAGE_KEY_CREATION));
|
||||
addPage(mKeySelectionPage = mPages[3] = new KeySelectionPage(this, PAGE_KEY_SELECTION));
|
||||
addPage(mKeyCheckPage = mPages[4] = new KeyCheckPage(this, PAGE_KEY_CHECK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performFinish() {
|
||||
// first we make sure export is fine if the destination file already exists
|
||||
File f = new File(mDestinationPath);
|
||||
if (f.isFile()) {
|
||||
if (AdtPlugin.displayPrompt("Export Wizard",
|
||||
"File already exists. Do you want to overwrite it?") == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// save the properties
|
||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_KEYSTORE, mKeystore);
|
||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_ALIAS, mKeyAlias);
|
||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_DESTINATION, mDestinationPath);
|
||||
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(mDestinationPath);
|
||||
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
|
||||
|
||||
// get the input file.
|
||||
FileInputStream fis = new FileInputStream(mApkFilePath);
|
||||
try {
|
||||
builder.writeZip(fis, null /* filter */);
|
||||
} finally {
|
||||
if (mKeystoreCreationMode || mKeyCreationMode) {
|
||||
final ArrayList<String> output = new ArrayList<String>();
|
||||
if (KeystoreHelper.createNewStore(
|
||||
mKeystore,
|
||||
null /*storeType*/,
|
||||
mKeystorePassword,
|
||||
mKeyAlias,
|
||||
mKeyPassword,
|
||||
mDName,
|
||||
mValidity,
|
||||
new IKeyGenOutput() {
|
||||
public void err(String message) {
|
||||
output.add(message);
|
||||
}
|
||||
public void out(String message) {
|
||||
output.add(message);
|
||||
}
|
||||
}) == false) {
|
||||
// keystore creation error!
|
||||
displayError(output.toArray(new String[output.size()]));
|
||||
return false;
|
||||
}
|
||||
|
||||
// keystore is created, now load the private key and certificate.
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
FileInputStream fis = new FileInputStream(mKeystore);
|
||||
keyStore.load(fis, mKeystorePassword.toCharArray());
|
||||
fis.close();
|
||||
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
|
||||
mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray()));
|
||||
|
||||
if (entry != null) {
|
||||
mPrivateKey = entry.getPrivateKey();
|
||||
mCertificate = (X509Certificate)entry.getCertificate();
|
||||
} else {
|
||||
// this really shouldn't happen since we now let the user choose the key
|
||||
// from a list read from the store.
|
||||
displayError("Could not find key");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
builder.close();
|
||||
fos.close();
|
||||
|
||||
return true;
|
||||
// check the private key/certificate again since it may have been created just above.
|
||||
if (mPrivateKey != null && mCertificate != null) {
|
||||
FileOutputStream fos = new FileOutputStream(mDestinationPath);
|
||||
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
|
||||
|
||||
// get the input file.
|
||||
FileInputStream fis = new FileInputStream(mApkFilePath);
|
||||
try {
|
||||
builder.writeZip(fis, null /* filter */);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
builder.close();
|
||||
fos.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
displayError(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
displayError(e);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
displayError(e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
displayError(e);
|
||||
} catch (KeytoolException e) {
|
||||
displayError(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -152,8 +278,13 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
|
||||
@Override
|
||||
public boolean canFinish() {
|
||||
// check if we have the apk to resign, the destination location, and either
|
||||
// a private key/certificate or the creation mode. In creation mode, unless
|
||||
// all the key/keystore info is valid, the user cannot reach the last page, so there's
|
||||
// no need to check them again here.
|
||||
return mApkFilePath != null &&
|
||||
mPrivateKey != null && mCertificate != null &&
|
||||
((mPrivateKey != null && mCertificate != null)
|
||||
|| mKeystoreCreationMode || mKeyCreationMode) &&
|
||||
mDestinationPath != null;
|
||||
}
|
||||
|
||||
@@ -175,6 +306,22 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
}
|
||||
}
|
||||
|
||||
ExportWizardPage getKeystoreSelectionPage() {
|
||||
return mKeystoreSelectionPage;
|
||||
}
|
||||
|
||||
ExportWizardPage getKeyCreationPage() {
|
||||
return mKeyCreationPage;
|
||||
}
|
||||
|
||||
ExportWizardPage getKeySelectionPage() {
|
||||
return mKeySelectionPage;
|
||||
}
|
||||
|
||||
ExportWizardPage getKeyCheckPage() {
|
||||
return mKeyCheckPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image descriptor for the wizard logo.
|
||||
*/
|
||||
@@ -192,10 +339,7 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
mApkFilePath = apkFilePath;
|
||||
mApkFileName = filename;
|
||||
|
||||
// indicate to the page that the project was changed.
|
||||
for (ExportWizardPage page : mPages) {
|
||||
page.newProjectReference();
|
||||
}
|
||||
updatePageOnChange(ExportWizardPage.DATA_PROJECT);
|
||||
}
|
||||
|
||||
String getApkFilename() {
|
||||
@@ -206,42 +350,95 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
mKeystore = path;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
|
||||
}
|
||||
|
||||
String getKeystore() {
|
||||
return mKeystore;
|
||||
}
|
||||
|
||||
void setKeystoreCreationMode(boolean createStore) {
|
||||
mKeystoreCreationMode = createStore;
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
|
||||
}
|
||||
|
||||
boolean getKeystoreCreationMode() {
|
||||
return mKeystoreCreationMode;
|
||||
}
|
||||
|
||||
|
||||
void setKeystorePassword(String password) {
|
||||
mKeystorePassword = password;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
|
||||
}
|
||||
|
||||
String getKeystorePassword() {
|
||||
return mKeystorePassword;
|
||||
}
|
||||
|
||||
void setKeyCreationMode(boolean createKey) {
|
||||
mKeyCreationMode = createKey;
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEY);
|
||||
}
|
||||
|
||||
boolean getKeyCreationMode() {
|
||||
return mKeyCreationMode;
|
||||
}
|
||||
|
||||
void setExistingAliases(List<String> aliases) {
|
||||
mExistingAliases = aliases;
|
||||
}
|
||||
|
||||
List<String> getExistingAliases() {
|
||||
return mExistingAliases;
|
||||
}
|
||||
|
||||
void setKeyAlias(String name) {
|
||||
mKeyAlias = name;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEY);
|
||||
}
|
||||
|
||||
String getKeyAlias() {
|
||||
return mKeyAlias;
|
||||
}
|
||||
|
||||
void setKeystorePassword(char[] password) {
|
||||
mKeystorePassword = password;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
}
|
||||
|
||||
char[] getKeystorePassword() {
|
||||
return mKeystorePassword;
|
||||
}
|
||||
|
||||
void setKeyPassword(char[] password) {
|
||||
|
||||
void setKeyPassword(String password) {
|
||||
mKeyPassword = password;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEY);
|
||||
}
|
||||
|
||||
char[] getKeyPassword() {
|
||||
String getKeyPassword() {
|
||||
return mKeyPassword;
|
||||
}
|
||||
|
||||
void setValidity(int validity) {
|
||||
mValidity = validity;
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEY);
|
||||
}
|
||||
|
||||
int getValidity() {
|
||||
return mValidity;
|
||||
}
|
||||
|
||||
void setDName(String dName) {
|
||||
mDName = dName;
|
||||
updatePageOnChange(ExportWizardPage.DATA_KEY);
|
||||
}
|
||||
|
||||
String getDName() {
|
||||
return mDName;
|
||||
}
|
||||
|
||||
void setSigningInfo(PrivateKey privateKey, X509Certificate certificate) {
|
||||
mPrivateKey = privateKey;
|
||||
mCertificate = certificate;
|
||||
@@ -251,4 +448,54 @@ public class ExportWizard extends Wizard implements IExportWizard {
|
||||
mDestinationPath = path;
|
||||
}
|
||||
|
||||
void updatePageOnChange(int changeMask) {
|
||||
for (ExportWizardPage page : mPages) {
|
||||
page.projectDataChanged(changeMask);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayError(String... messages) {
|
||||
String message = null;
|
||||
if (messages.length == 1) {
|
||||
message = messages[0];
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(messages[0]);
|
||||
for (int i = 1; i < messages.length; i++) {
|
||||
sb.append('\n');
|
||||
sb.append(messages[i]);
|
||||
}
|
||||
|
||||
message = sb.toString();
|
||||
}
|
||||
|
||||
AdtPlugin.displayError("Export Wizard", message);
|
||||
}
|
||||
|
||||
private void displayError(Exception e) {
|
||||
String message = getExceptionMessage(e);
|
||||
displayError(message);
|
||||
|
||||
AdtPlugin.log(e, "Export Wizard Error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns
|
||||
* <code>null</code>, the method is called again on the cause of the Throwable object.
|
||||
* <p/>If no Throwable in the chain has a valid message, the canonical name of the first
|
||||
* exception is returned.
|
||||
*/
|
||||
private static String getExceptionMessage(Throwable t) {
|
||||
String message = t.getMessage();
|
||||
if (message == null) {
|
||||
Throwable cause = t.getCause();
|
||||
if (cause != null) {
|
||||
return getExceptionMessage(cause);
|
||||
}
|
||||
|
||||
// no more cause and still no message. display the first exception.
|
||||
return cause.getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,275 +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.project.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.FileDialog;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.UnrecoverableEntryException;
|
||||
import java.security.KeyStore.PrivateKeyEntry;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class FinalExportPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private PrivateKey mPrivateKey;
|
||||
private X509Certificate mCertificate;
|
||||
private Text mDestination;
|
||||
private Button mBrowseButton;
|
||||
private boolean mFatalSigningError;
|
||||
private FormText mDetailText;
|
||||
|
||||
protected FinalExportPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Application Export");
|
||||
setDescription("Export the signed Application package.");
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
// build the ui.
|
||||
Composite composite = new Composite(parent, SWT.NULL);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
GridLayout gl = new GridLayout(3, false);
|
||||
gl.verticalSpacing *= 3;
|
||||
composite.setLayout(gl);
|
||||
|
||||
GridData gd;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Destination APK file:");
|
||||
mDestination = new Text(composite, SWT.BORDER);
|
||||
mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mDestination.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
onDestinationChange();
|
||||
}
|
||||
});
|
||||
mBrowseButton = new Button(composite, SWT.PUSH);
|
||||
mBrowseButton.setText("Browse...");
|
||||
mBrowseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog fileDialog = new FileDialog(mBrowseButton.getShell(), SWT.SAVE);
|
||||
|
||||
fileDialog.setText("Destination file name");
|
||||
fileDialog.setFileName(mWizard.getApkFilename());
|
||||
|
||||
String saveLocation = fileDialog.open();
|
||||
if (saveLocation != null) {
|
||||
mDestination.setText(saveLocation);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mDetailText = new FormText(composite, SWT.NONE);
|
||||
mDetailText.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
|
||||
gd.horizontalSpan = 3;
|
||||
|
||||
setControl(composite);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if (mNewProjectReference) {
|
||||
// reset the destination from the content of the project
|
||||
IProject project = mWizard.getProject();
|
||||
|
||||
String destination = ProjectHelper.loadStringProperty(project,
|
||||
ExportWizard.PROPERTY_DESTINATION);
|
||||
if (destination != null) {
|
||||
mDestination.setText(destination);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the wizard with no key/cert to make it not finishable, unless a valid
|
||||
// key/cert is found.
|
||||
mWizard.setSigningInfo(null, null);
|
||||
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
FileInputStream fis = new FileInputStream(mWizard.getKeystore());
|
||||
keyStore.load(fis, mWizard.getKeystorePassword());
|
||||
fis.close();
|
||||
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
|
||||
mWizard.getKeyAlias(),
|
||||
new KeyStore.PasswordProtection(mWizard.getKeyPassword()));
|
||||
|
||||
if (entry != null) {
|
||||
mPrivateKey = entry.getPrivateKey();
|
||||
mCertificate = (X509Certificate)entry.getCertificate();
|
||||
} else {
|
||||
setErrorMessage("Unable to find key");
|
||||
|
||||
setPageComplete(false);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// this was checked at the first previous step and will not happen here, unless
|
||||
// the file was removed during the export wizard execution.
|
||||
onException(e);
|
||||
} catch (KeyStoreException e) {
|
||||
onException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
onException(e);
|
||||
} catch (UnrecoverableEntryException e) {
|
||||
onException(e);
|
||||
} catch (CertificateException e) {
|
||||
onException(e);
|
||||
} catch (IOException e) {
|
||||
onException(e);
|
||||
}
|
||||
|
||||
if (mPrivateKey != null && mCertificate != null) {
|
||||
mFatalSigningError = false;
|
||||
|
||||
Calendar expirationCalendar = Calendar.getInstance();
|
||||
expirationCalendar.setTime(mCertificate.getNotAfter());
|
||||
Calendar today = Calendar.getInstance();
|
||||
|
||||
if (expirationCalendar.before(today)) {
|
||||
mDetailText.setText(String.format("<form><p>Certificate expired on %s</p></form>",
|
||||
mCertificate.getNotAfter().toString()),
|
||||
true /* parseTags */, true /* expandURLs */);
|
||||
|
||||
// fatal error = nothing can make the page complete.
|
||||
mFatalSigningError = true;
|
||||
|
||||
setErrorMessage("Certificate is expired!");
|
||||
setPageComplete(false);
|
||||
} else {
|
||||
// valid, key/cert: put it in the wizard so that it can be finished
|
||||
mWizard.setSigningInfo(mPrivateKey, mCertificate);
|
||||
|
||||
StringBuilder sb = new StringBuilder(String.format("<form><p>Certificate expires on %s.</p>",
|
||||
mCertificate.getNotAfter().toString()));
|
||||
|
||||
int expirationYear = expirationCalendar.get(Calendar.YEAR);
|
||||
int thisYear = today.get(Calendar.YEAR);
|
||||
|
||||
if (thisYear + 25 < expirationYear) {
|
||||
// do nothing
|
||||
} else {
|
||||
if (expirationYear == thisYear) {
|
||||
sb.append("<p>The certificate expires this year!</p>");
|
||||
} else {
|
||||
int count = expirationYear-thisYear;
|
||||
sb.append(String.format("<p>The Certificate expires in %1$s %2$s!</p>",
|
||||
count, count == 1 ? "year" : "years"));
|
||||
}
|
||||
|
||||
sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>");
|
||||
sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>");
|
||||
sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, ");
|
||||
sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>");
|
||||
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
||||
|
||||
sb.append("</form>");
|
||||
}
|
||||
|
||||
mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */);
|
||||
}
|
||||
mDetailText.getParent().layout();
|
||||
} else {
|
||||
// fatal error = nothing can make the page complete.
|
||||
mFatalSigningError = true;
|
||||
}
|
||||
|
||||
onDestinationChange();
|
||||
}
|
||||
|
||||
private void onDestinationChange() {
|
||||
if (mFatalSigningError == false) {
|
||||
String path = mDestination.getText().trim();
|
||||
if (path.length() == 0) {
|
||||
setErrorMessage("Enter destination for the APK file.");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
} else {
|
||||
File file = new File(path);
|
||||
if (file.isDirectory()) {
|
||||
setErrorMessage("Destination is a directory!");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
} else {
|
||||
File parentFile = file.getParentFile();
|
||||
if (parentFile == null || parentFile.isDirectory() == false) {
|
||||
setErrorMessage("Not a valid directory.");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
} else {
|
||||
mWizard.setDestination(path);
|
||||
setErrorMessage(null);
|
||||
setPageComplete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
|
||||
* {@link Throwable} object. If the {@link Throwable#getMessage()} returns <code>null</code>,
|
||||
* the method is called again on the cause of the Throwable.
|
||||
*/
|
||||
private void onException(Throwable t) {
|
||||
String message = t.getMessage();
|
||||
if (message == null) {
|
||||
Throwable cause = t.getCause();
|
||||
if (cause != null) {
|
||||
onException(cause);
|
||||
} else {
|
||||
// no more cause and still no message. display the first exception.
|
||||
setErrorMessage(cause.getClass().getCanonicalName());
|
||||
setPageComplete(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setErrorMessage(message);
|
||||
setPageComplete(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.project.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.FileDialog;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.UnrecoverableEntryException;
|
||||
import java.security.KeyStore.PrivateKeyEntry;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Final page of the wizard that checks the key and ask for the ouput location.
|
||||
*/
|
||||
final class KeyCheckPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private PrivateKey mPrivateKey;
|
||||
private X509Certificate mCertificate;
|
||||
private Text mDestination;
|
||||
private boolean mFatalSigningError;
|
||||
private FormText mDetailText;
|
||||
|
||||
protected KeyCheckPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Destination and key/certificate checks");
|
||||
setDescription(""); // TODO
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
// build the ui.
|
||||
Composite composite = new Composite(parent, SWT.NULL);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
GridLayout gl = new GridLayout(3, false);
|
||||
gl.verticalSpacing *= 3;
|
||||
composite.setLayout(gl);
|
||||
|
||||
GridData gd;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Destination APK file:");
|
||||
mDestination = new Text(composite, SWT.BORDER);
|
||||
mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mDestination.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
onDestinationChange();
|
||||
}
|
||||
});
|
||||
final Button browseButton = new Button(composite, SWT.PUSH);
|
||||
browseButton.setText("Browse...");
|
||||
browseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE);
|
||||
|
||||
fileDialog.setText("Destination file name");
|
||||
fileDialog.setFileName(mWizard.getApkFilename());
|
||||
|
||||
String saveLocation = fileDialog.open();
|
||||
if (saveLocation != null) {
|
||||
mDestination.setText(saveLocation);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mDetailText = new FormText(composite, SWT.NONE);
|
||||
mDetailText.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
|
||||
gd.horizontalSpan = 3;
|
||||
|
||||
setControl(composite);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if ((mProjectDataChanged & DATA_PROJECT) != 0) {
|
||||
// reset the destination from the content of the project
|
||||
IProject project = mWizard.getProject();
|
||||
|
||||
String destination = ProjectHelper.loadStringProperty(project,
|
||||
ExportWizard.PROPERTY_DESTINATION);
|
||||
if (destination != null) {
|
||||
mDestination.setText(destination);
|
||||
}
|
||||
}
|
||||
|
||||
// if anything change we basically reload the data.
|
||||
if (mProjectDataChanged != 0) {
|
||||
mFatalSigningError = false;
|
||||
|
||||
// reset the wizard with no key/cert to make it not finishable, unless a valid
|
||||
// key/cert is found.
|
||||
mWizard.setSigningInfo(null, null);
|
||||
|
||||
if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) {
|
||||
int validity = mWizard.getValidity();
|
||||
StringBuilder sb = new StringBuilder(
|
||||
String.format("<form><p>Certificate expires in %d years.</p>",
|
||||
validity));
|
||||
|
||||
if (validity < 25) {
|
||||
sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>");
|
||||
sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>");
|
||||
sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, ");
|
||||
sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>");
|
||||
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
||||
}
|
||||
|
||||
sb.append("</form>");
|
||||
mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */);
|
||||
} else {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
FileInputStream fis = new FileInputStream(mWizard.getKeystore());
|
||||
keyStore.load(fis, mWizard.getKeystorePassword().toCharArray());
|
||||
fis.close();
|
||||
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
|
||||
mWizard.getKeyAlias(),
|
||||
new KeyStore.PasswordProtection(
|
||||
mWizard.getKeyPassword().toCharArray()));
|
||||
|
||||
if (entry != null) {
|
||||
mPrivateKey = entry.getPrivateKey();
|
||||
mCertificate = (X509Certificate)entry.getCertificate();
|
||||
} else {
|
||||
setErrorMessage("Unable to find key.");
|
||||
|
||||
setPageComplete(false);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// this was checked at the first previous step and will not happen here, unless
|
||||
// the file was removed during the export wizard execution.
|
||||
onException(e);
|
||||
} catch (KeyStoreException e) {
|
||||
onException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
onException(e);
|
||||
} catch (UnrecoverableEntryException e) {
|
||||
onException(e);
|
||||
} catch (CertificateException e) {
|
||||
onException(e);
|
||||
} catch (IOException e) {
|
||||
onException(e);
|
||||
}
|
||||
|
||||
if (mPrivateKey != null && mCertificate != null) {
|
||||
Calendar expirationCalendar = Calendar.getInstance();
|
||||
expirationCalendar.setTime(mCertificate.getNotAfter());
|
||||
Calendar today = Calendar.getInstance();
|
||||
|
||||
if (expirationCalendar.before(today)) {
|
||||
mDetailText.setText(String.format(
|
||||
"<form><p>Certificate expired on %s</p></form>",
|
||||
mCertificate.getNotAfter().toString()),
|
||||
true /* parseTags */, true /* expandURLs */);
|
||||
|
||||
// fatal error = nothing can make the page complete.
|
||||
mFatalSigningError = true;
|
||||
|
||||
setErrorMessage("Certificate is expired.");
|
||||
setPageComplete(false);
|
||||
} else {
|
||||
// valid, key/cert: put it in the wizard so that it can be finished
|
||||
mWizard.setSigningInfo(mPrivateKey, mCertificate);
|
||||
|
||||
StringBuilder sb = new StringBuilder(String.format(
|
||||
"<form><p>Certificate expires on %s.</p>",
|
||||
mCertificate.getNotAfter().toString()));
|
||||
|
||||
int expirationYear = expirationCalendar.get(Calendar.YEAR);
|
||||
int thisYear = today.get(Calendar.YEAR);
|
||||
|
||||
if (thisYear + 25 < expirationYear) {
|
||||
// do nothing
|
||||
} else {
|
||||
if (expirationYear == thisYear) {
|
||||
sb.append("<p>The certificate expires this year.</p>");
|
||||
} else {
|
||||
int count = expirationYear-thisYear;
|
||||
sb.append(String.format(
|
||||
"<p>The Certificate expires in %1$s %2$s.</p>",
|
||||
count, count == 1 ? "year" : "years"));
|
||||
}
|
||||
|
||||
sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>");
|
||||
sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>");
|
||||
sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, ");
|
||||
sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>");
|
||||
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
||||
}
|
||||
|
||||
sb.append("</form>");
|
||||
|
||||
mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */);
|
||||
}
|
||||
mDetailText.getParent().layout();
|
||||
} else {
|
||||
// fatal error = nothing can make the page complete.
|
||||
mFatalSigningError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDestinationChange();
|
||||
}
|
||||
|
||||
private void onDestinationChange() {
|
||||
if (mFatalSigningError == false) {
|
||||
// reset messages for now.
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
String path = mDestination.getText().trim();
|
||||
|
||||
if (path.length() == 0) {
|
||||
setErrorMessage("Enter destination for the APK file.");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File(path);
|
||||
if (file.isDirectory()) {
|
||||
setErrorMessage("Destination is a directory.");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
File parentFile = file.getParentFile();
|
||||
if (parentFile == null || parentFile.isDirectory() == false) {
|
||||
setErrorMessage("Not a valid directory.");
|
||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// no error, set the destination in the wizard.
|
||||
mWizard.setDestination(path);
|
||||
setPageComplete(true);
|
||||
|
||||
// However, we should also test if the file already exists.
|
||||
if (file.isFile()) {
|
||||
setMessage("Destination file already exists.", WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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.project.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.jface.wizard.IWizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.VerifyEvent;
|
||||
import org.eclipse.swt.events.VerifyListener;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Key creation page.
|
||||
*/
|
||||
final class KeyCreationPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private Text mAlias;
|
||||
private Text mKeyPassword;
|
||||
private Text mKeyPassword2;
|
||||
private Text mCnField;
|
||||
private boolean mDisableOnChange = false;
|
||||
private Text mOuField;
|
||||
private Text mOField;
|
||||
private Text mLField;
|
||||
private Text mStField;
|
||||
private Text mCField;
|
||||
private String mDName;
|
||||
private int mValidity = 0;
|
||||
private List<String> mExistingAliases;
|
||||
|
||||
|
||||
protected KeyCreationPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Key Creation");
|
||||
setDescription(""); // TODO?
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
Composite composite = new Composite(parent, SWT.NULL);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
GridLayout gl = new GridLayout(2, false);
|
||||
composite.setLayout(gl);
|
||||
|
||||
GridData gd;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Alias:");
|
||||
mAlias = new Text(composite, SWT.BORDER);
|
||||
mAlias.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Password:");
|
||||
mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeyPassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mKeyPassword.addVerifyListener(sPasswordVerifier);
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Confirm:");
|
||||
mKeyPassword2 = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeyPassword2.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mKeyPassword2.addVerifyListener(sPasswordVerifier);
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Validity (years):");
|
||||
final Text validityText = new Text(composite, SWT.BORDER);
|
||||
validityText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
validityText.addVerifyListener(new VerifyListener() {
|
||||
public void verifyText(VerifyEvent e) {
|
||||
// check for digit only.
|
||||
for (int i = 0 ; i < e.text.length(); i++) {
|
||||
char letter = e.text.charAt(i);
|
||||
if (letter < '0' || letter > '9') {
|
||||
e.doit = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 2;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("First and Last Name:");
|
||||
mCnField = new Text(composite, SWT.BORDER);
|
||||
mCnField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Organizational Unit:");
|
||||
mOuField = new Text(composite, SWT.BORDER);
|
||||
mOuField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Organization:");
|
||||
mOField = new Text(composite, SWT.BORDER);
|
||||
mOField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("City or Locality:");
|
||||
mLField = new Text(composite, SWT.BORDER);
|
||||
mLField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("State or Province:");
|
||||
mStField = new Text(composite, SWT.BORDER);
|
||||
mStField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Country Code (XX):");
|
||||
mCField = new Text(composite, SWT.BORDER);
|
||||
mCField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setControl(composite);
|
||||
|
||||
mAlias.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyAlias(mAlias.getText().trim());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
mKeyPassword.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyPassword(mKeyPassword.getText());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
mKeyPassword2.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
validityText.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
try {
|
||||
mValidity = Integer.parseInt(validityText.getText());
|
||||
} catch (NumberFormatException e2) {
|
||||
// this should only happen if the text field is empty due to the verifyListener.
|
||||
mValidity = 0;
|
||||
}
|
||||
mWizard.setValidity(mValidity);
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
ModifyListener dNameListener = new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
onDNameChange();
|
||||
}
|
||||
};
|
||||
|
||||
mCnField.addModifyListener(dNameListener);
|
||||
mOuField.addModifyListener(dNameListener);
|
||||
mOField.addModifyListener(dNameListener);
|
||||
mLField.addModifyListener(dNameListener);
|
||||
mStField.addModifyListener(dNameListener);
|
||||
mCField.addModifyListener(dNameListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if ((mProjectDataChanged & (DATA_PROJECT | DATA_KEYSTORE)) != 0) {
|
||||
// reset the keystore/alias from the content of the project
|
||||
IProject project = mWizard.getProject();
|
||||
|
||||
// disable onChange for now. we'll call it once at the end.
|
||||
mDisableOnChange = true;
|
||||
|
||||
String alias = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_ALIAS);
|
||||
if (alias != null) {
|
||||
mAlias.setText(alias);
|
||||
}
|
||||
|
||||
// get the existing list of keys if applicable
|
||||
if (mWizard.getKeyCreationMode()) {
|
||||
mExistingAliases = mWizard.getExistingAliases();
|
||||
} else {
|
||||
mExistingAliases = null;
|
||||
}
|
||||
|
||||
// reset the passwords
|
||||
mKeyPassword.setText(""); //$NON-NLS-1$
|
||||
mKeyPassword2.setText(""); //$NON-NLS-1$
|
||||
|
||||
// enable onChange, and call it to display errors and enable/disable pageCompleted.
|
||||
mDisableOnChange = false;
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWizardPage getPreviousPage() {
|
||||
if (mWizard.getKeyCreationMode()) { // this means we create a key from an existing store
|
||||
return mWizard.getKeySelectionPage();
|
||||
}
|
||||
|
||||
return mWizard.getKeystoreSelectionPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWizardPage getNextPage() {
|
||||
return mWizard.getKeyCheckPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes and update the error message and calls {@link #setPageComplete(boolean)}.
|
||||
*/
|
||||
private void onChange() {
|
||||
if (mDisableOnChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
if (mAlias.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key alias.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else if (mExistingAliases != null) {
|
||||
// we cannot use indexOf, because we need to do a case-insensitive check
|
||||
String keyAlias = mAlias.getText().trim();
|
||||
for (String alias : mExistingAliases) {
|
||||
if (alias.equalsIgnoreCase(keyAlias)) {
|
||||
setErrorMessage("Key alias already exists in keystore.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String value = mKeyPassword.getText();
|
||||
if (value.length() == 0) {
|
||||
setErrorMessage("Enter key password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else if (value.length() < 6) {
|
||||
setErrorMessage("Key password is too short - must be at least 6 characters.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.equals(mKeyPassword2.getText()) == false) {
|
||||
setErrorMessage("Key passwords don't match.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mValidity == 0) {
|
||||
setErrorMessage("Key certificate validity is required.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else if (mValidity < 25) {
|
||||
setMessage("A 25 year certificate validity is recommended.", WARNING);
|
||||
} else if (mValidity > 1000) {
|
||||
setErrorMessage("Key certificate validity must be between 1 and 1000 years.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDName == null || mDName.length() == 0) {
|
||||
setErrorMessage("At least one Certificate issuer field is required to be non-empty.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setPageComplete(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in the DName fields.
|
||||
*/
|
||||
private void onDNameChange() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
buildDName("CN", mCnField, sb);
|
||||
buildDName("OU", mOuField, sb);
|
||||
buildDName("O", mOField, sb);
|
||||
buildDName("L", mLField, sb);
|
||||
buildDName("ST", mStField, sb);
|
||||
buildDName("C", mCField, sb);
|
||||
|
||||
mDName = sb.toString();
|
||||
mWizard.setDName(mDName);
|
||||
|
||||
onChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the distinguished name string with the provided {@link StringBuilder}.
|
||||
* @param prefix the prefix of the entry.
|
||||
* @param textField The {@link Text} field containing the entry value.
|
||||
* @param sb the string builder containing the dname.
|
||||
*/
|
||||
private void buildDName(String prefix, Text textField, StringBuilder sb) {
|
||||
if (textField != null) {
|
||||
String value = textField.getText().trim();
|
||||
if (value.length() > 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
sb.append(prefix);
|
||||
sb.append('=');
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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.project.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.jface.wizard.IWizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Combo;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Key Selection Page. This is used when an existing keystore is used.
|
||||
*/
|
||||
final class KeySelectionPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private Label mKeyAliasesLabel;
|
||||
private Combo mKeyAliases;
|
||||
private Label mKeyPasswordLabel;
|
||||
private Text mKeyPassword;
|
||||
private boolean mDisableOnChange = false;
|
||||
private Button mUseExistingKey;
|
||||
private Button mCreateKey;
|
||||
|
||||
protected KeySelectionPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Key alias selection");
|
||||
setDescription(""); // TODO
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
Composite composite = new Composite(parent, SWT.NULL);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
GridLayout gl = new GridLayout(3, false);
|
||||
composite.setLayout(gl);
|
||||
|
||||
GridData gd;
|
||||
|
||||
mUseExistingKey = new Button(composite, SWT.RADIO);
|
||||
mUseExistingKey.setText("Use existing key");
|
||||
mUseExistingKey.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
mUseExistingKey.setSelection(true);
|
||||
|
||||
new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData());
|
||||
gd.heightHint = 0;
|
||||
gd.widthHint = 50;
|
||||
mKeyAliasesLabel = new Label(composite, SWT.NONE);
|
||||
mKeyAliasesLabel.setText("Alias:");
|
||||
mKeyAliases = new Combo(composite, SWT.READ_ONLY);
|
||||
mKeyAliases.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData());
|
||||
gd.heightHint = 0;
|
||||
gd.widthHint = 50;
|
||||
mKeyPasswordLabel = new Label(composite, SWT.NONE);
|
||||
mKeyPasswordLabel.setText("Password:");
|
||||
mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeyPassword.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
mCreateKey = new Button(composite, SWT.RADIO);
|
||||
mCreateKey.setText("Create new key");
|
||||
mCreateKey.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setControl(composite);
|
||||
|
||||
mUseExistingKey.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mWizard.setKeyCreationMode(!mUseExistingKey.getSelection());
|
||||
enableWidgets();
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
mKeyAliases.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mWizard.setKeyAlias(mKeyAliases.getItem(mKeyAliases.getSelectionIndex()));
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
mKeyPassword.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyPassword(mKeyPassword.getText());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if ((mProjectDataChanged & (DATA_PROJECT | DATA_KEYSTORE)) != 0) {
|
||||
// disable onChange for now. we'll call it once at the end.
|
||||
mDisableOnChange = true;
|
||||
|
||||
// reset the alias from the content of the project
|
||||
try {
|
||||
// reset to using a key
|
||||
mWizard.setKeyCreationMode(false);
|
||||
mUseExistingKey.setSelection(true);
|
||||
mCreateKey.setSelection(false);
|
||||
enableWidgets();
|
||||
|
||||
// remove the content of the alias combo always and first, in case the
|
||||
// keystore password is wrong
|
||||
mKeyAliases.removeAll();
|
||||
|
||||
// get the alias list (also used as a keystore password test)
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
FileInputStream fis = new FileInputStream(mWizard.getKeystore());
|
||||
keyStore.load(fis, mWizard.getKeystorePassword().toCharArray());
|
||||
fis.close();
|
||||
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
|
||||
// get the alias from the project previous export, and look for a match as
|
||||
// we add the aliases to the combo.
|
||||
IProject project = mWizard.getProject();
|
||||
|
||||
String keyAlias = ProjectHelper.loadStringProperty(project,
|
||||
ExportWizard.PROPERTY_ALIAS);
|
||||
|
||||
ArrayList<String> aliasList = new ArrayList<String>();
|
||||
|
||||
int selection = -1;
|
||||
int count = 0;
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = aliases.nextElement();
|
||||
mKeyAliases.add(alias);
|
||||
aliasList.add(alias);
|
||||
if (selection == -1 && alias.equalsIgnoreCase(keyAlias)) {
|
||||
selection = count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
mWizard.setExistingAliases(aliasList);
|
||||
|
||||
if (selection != -1) {
|
||||
mKeyAliases.select(selection);
|
||||
|
||||
// since a match was found and is selected, we need to give it to
|
||||
// the wizard as well
|
||||
mWizard.setKeyAlias(keyAlias);
|
||||
} else {
|
||||
mKeyAliases.clearSelection();
|
||||
}
|
||||
|
||||
// reset the password
|
||||
mKeyPassword.setText(""); //$NON-NLS-1$
|
||||
|
||||
// enable onChange, and call it to display errors and enable/disable pageCompleted.
|
||||
mDisableOnChange = false;
|
||||
onChange();
|
||||
} catch (KeyStoreException e) {
|
||||
onException(e);
|
||||
} catch (FileNotFoundException e) {
|
||||
onException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
onException(e);
|
||||
} catch (CertificateException e) {
|
||||
onException(e);
|
||||
} catch (IOException e) {
|
||||
onException(e);
|
||||
} finally {
|
||||
// in case we exit with an exception, we need to reset this
|
||||
mDisableOnChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWizardPage getPreviousPage() {
|
||||
return mWizard.getKeystoreSelectionPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWizardPage getNextPage() {
|
||||
if (mWizard.getKeyCreationMode()) {
|
||||
return mWizard.getKeyCreationPage();
|
||||
}
|
||||
|
||||
return mWizard.getKeyCheckPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes and update the error message and calls {@link #setPageComplete(boolean)}.
|
||||
*/
|
||||
private void onChange() {
|
||||
if (mDisableOnChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
if (mWizard.getKeyCreationMode() == false) {
|
||||
if (mKeyAliases.getSelectionIndex() == -1) {
|
||||
setErrorMessage("Select a key alias.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeyPassword.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setPageComplete(true);
|
||||
}
|
||||
|
||||
private void enableWidgets() {
|
||||
boolean useKey = !mWizard.getKeyCreationMode();
|
||||
mKeyAliasesLabel.setEnabled(useKey);
|
||||
mKeyAliases.setEnabled(useKey);
|
||||
mKeyPassword.setEnabled(useKey);
|
||||
mKeyPasswordLabel.setEnabled(useKey);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.jface.wizard.IWizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
@@ -36,23 +37,26 @@ import org.eclipse.swt.widgets.Text;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Second export wizard page.
|
||||
* Keystore selection page. This page allows to choose to create a new keystore or use an
|
||||
* existing one.
|
||||
*/
|
||||
public class SigningExportPage extends ExportWizardPage {
|
||||
final class KeystoreSelectionPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private Button mUseExistingKeystore;
|
||||
private Button mCreateKeystore;
|
||||
private Text mKeystore;
|
||||
private Text mAlias;
|
||||
private Text mKeystorePassword;
|
||||
private Text mKeyPassword;
|
||||
private Label mConfirmLabel;
|
||||
private Text mKeystorePassword2;
|
||||
private boolean mDisableOnChange = false;
|
||||
|
||||
protected SigningExportPage(ExportWizard wizard, String pageName) {
|
||||
protected KeystoreSelectionPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Application Signing");
|
||||
setDescription("Defines which store, key and certificate to use to sign the Android Application.");
|
||||
setTitle("Keystore selection");
|
||||
setDescription(""); //TODO
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
@@ -62,8 +66,19 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
composite.setLayout(gl);
|
||||
|
||||
GridData gd;
|
||||
|
||||
mUseExistingKeystore = new Button(composite, SWT.RADIO);
|
||||
mUseExistingKeystore.setText("Use existing keystore");
|
||||
mUseExistingKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
mUseExistingKeystore.setSelection(true);
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Keystore:");
|
||||
mCreateKeystore = new Button(composite, SWT.RADIO);
|
||||
mCreateKeystore.setText("Create new keystore");
|
||||
mCreateKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Location:");
|
||||
mKeystore = new Text(composite, SWT.BORDER);
|
||||
mKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
final Button browseButton = new Button(composite, SWT.PUSH);
|
||||
@@ -71,8 +86,14 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
browseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.OPEN);
|
||||
fileDialog.setText("Load Keystore");
|
||||
FileDialog fileDialog;
|
||||
if (mUseExistingKeystore.getSelection()) {
|
||||
fileDialog = new FileDialog(browseButton.getShell(),SWT.OPEN);
|
||||
fileDialog.setText("Load Keystore");
|
||||
} else {
|
||||
fileDialog = new FileDialog(browseButton.getShell(),SWT.SAVE);
|
||||
fileDialog.setText("Select Keystore Name");
|
||||
}
|
||||
|
||||
String fileName = fileDialog.open();
|
||||
if (fileName != null) {
|
||||
@@ -80,64 +101,73 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData());
|
||||
gd.horizontalSpan = 2;
|
||||
gd.heightHint = 0;
|
||||
new Button(composite, SWT.PUSH).setText("New...");
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Key Alias:");
|
||||
mAlias = new Text(composite, SWT.BORDER);
|
||||
mAlias.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
|
||||
new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Store password:");
|
||||
new Label(composite, SWT.NONE).setText("Password:");
|
||||
mKeystorePassword = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeystorePassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mKeystorePassword.addVerifyListener(sPasswordVerifier);
|
||||
new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData());
|
||||
gd.heightHint = gd.widthHint = 0;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Key password:");
|
||||
mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeyPassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mConfirmLabel = new Label(composite, SWT.NONE);
|
||||
mConfirmLabel.setText("Confirm:");
|
||||
mKeystorePassword2 = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeystorePassword2.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mKeystorePassword2.addVerifyListener(sPasswordVerifier);
|
||||
new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData());
|
||||
gd.heightHint = gd.widthHint = 0;
|
||||
mKeystorePassword2.setEnabled(false);
|
||||
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setControl(composite);
|
||||
|
||||
mUseExistingKeystore.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
boolean createStore = !mUseExistingKeystore.getSelection();
|
||||
mKeystorePassword2.setEnabled(createStore);
|
||||
mConfirmLabel.setEnabled(createStore);
|
||||
mWizard.setKeystoreCreationMode(createStore);
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
mKeystore.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeystore(mKeystore.getText().trim());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
mAlias.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyAlias(mAlias.getText().trim());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
|
||||
mKeystorePassword.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeystorePassword(mKeystorePassword.getText().trim().toCharArray());
|
||||
mWizard.setKeystorePassword(mKeystorePassword.getText());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
mKeyPassword.addModifyListener(new ModifyListener() {
|
||||
|
||||
mKeystorePassword2.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyPassword(mKeyPassword.getText().trim().toCharArray());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWizardPage getNextPage() {
|
||||
if (mUseExistingKeystore.getSelection()) {
|
||||
return mWizard.getKeySelectionPage();
|
||||
}
|
||||
|
||||
return mWizard.getKeyCreationPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if (mNewProjectReference) {
|
||||
if ((mProjectDataChanged & DATA_PROJECT) != 0) {
|
||||
// reset the keystore/alias from the content of the project
|
||||
IProject project = mWizard.getProject();
|
||||
|
||||
@@ -150,14 +180,9 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
mKeystore.setText(keystore);
|
||||
}
|
||||
|
||||
String alias = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_ALIAS);
|
||||
if (alias != null) {
|
||||
mAlias.setText(alias);
|
||||
}
|
||||
|
||||
// reset the passwords
|
||||
mKeystorePassword.setText(""); //$NON-NLS-1$
|
||||
mKeyPassword.setText(""); //$NON-NLS-1$
|
||||
mKeystorePassword2.setText(""); //$NON-NLS-1$
|
||||
|
||||
// enable onChange, and call it to display errors and enable/disable pageCompleted.
|
||||
mDisableOnChange = false;
|
||||
@@ -176,6 +201,8 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
boolean createStore = !mUseExistingKeystore.getSelection();
|
||||
|
||||
// checks the keystore path is non null.
|
||||
String keystore = mKeystore.getText().trim();
|
||||
if (keystore.length() == 0) {
|
||||
@@ -185,34 +212,49 @@ public class SigningExportPage extends ExportWizardPage {
|
||||
} else {
|
||||
File f = new File(keystore);
|
||||
if (f.exists() == false) {
|
||||
setErrorMessage("Keystore does not exists!");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
if (createStore == false) {
|
||||
setErrorMessage("Keystore does not exist.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
} else if (f.isDirectory()) {
|
||||
setErrorMessage("Keystore is a directory!");
|
||||
setErrorMessage("Keystore path is a directory.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else if (f.isFile()) {
|
||||
if (createStore) {
|
||||
setErrorMessage("File already exists.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mAlias.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key alias.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeystorePassword.getText().trim().length() == 0) {
|
||||
String value = mKeystorePassword.getText();
|
||||
if (value.length() == 0) {
|
||||
setErrorMessage("Enter keystore password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeyPassword.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key password.");
|
||||
} else if (createStore && value.length() < 6) {
|
||||
setErrorMessage("Keystore password is too short - must be at least 6 characters.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (createStore) {
|
||||
if (mKeystorePassword2.getText().length() == 0) {
|
||||
setErrorMessage("Confirm keystore password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeystorePassword.getText().equals(mKeystorePassword2.getText()) == false) {
|
||||
setErrorMessage("Keystore passwords do not match.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setPageComplete(true);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ import java.io.File;
|
||||
/**
|
||||
* First Export Wizard Page. Display warning/errors.
|
||||
*/
|
||||
public class PreExportPage extends ExportWizardPage {
|
||||
final class ProjectCheckPage extends ExportWizardPage {
|
||||
private final static String IMG_ERROR = "error.png"; //$NON-NLS-1$
|
||||
private final static String IMG_WARNING = "warning.png"; //$NON-NLS-1$
|
||||
|
||||
@@ -60,12 +60,13 @@ public class PreExportPage extends ExportWizardPage {
|
||||
private Composite mErrorComposite;
|
||||
private Text mProjectText;
|
||||
private ProjectChooserHelper mProjectChooserHelper;
|
||||
private boolean mFirstOnShow = true;
|
||||
|
||||
protected PreExportPage(ExportWizard wizard, String pageName) {
|
||||
protected ProjectCheckPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Pre Export Checks");
|
||||
setTitle("Project Checks");
|
||||
setDescription("Performs a set of checks to make sure the application can be exported.");
|
||||
}
|
||||
|
||||
@@ -123,10 +124,14 @@ public class PreExportPage extends ExportWizardPage {
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// get the project and init the ui
|
||||
IProject project = mWizard.getProject();
|
||||
if (project != null) {
|
||||
mProjectText.setText(project.getName());
|
||||
if (mFirstOnShow) {
|
||||
// get the project and init the ui
|
||||
IProject project = mWizard.getProject();
|
||||
if (project != null) {
|
||||
mProjectText.setText(project.getName());
|
||||
}
|
||||
|
||||
mFirstOnShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,16 +27,19 @@ class AndroidClasspathContainer implements IClasspathContainer {
|
||||
|
||||
private IClasspathEntry[] mClasspathEntry;
|
||||
private IPath mContainerPath;
|
||||
private String mName;
|
||||
|
||||
/**
|
||||
* Constructs the container with the {@link IClasspathEntry} representing the android
|
||||
* framework jar file and the container id
|
||||
* @param entry the entry representing the android framework.
|
||||
* @param path the path containing the classpath container id.
|
||||
* @param name the name of the container to display.
|
||||
*/
|
||||
AndroidClasspathContainer(IClasspathEntry entry, IPath path) {
|
||||
AndroidClasspathContainer(IClasspathEntry entry, IPath path, String name) {
|
||||
mClasspathEntry = new IClasspathEntry[] { entry };
|
||||
mContainerPath = path;
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public IClasspathEntry[] getClasspathEntries() {
|
||||
@@ -44,7 +47,7 @@ class AndroidClasspathContainer implements IClasspathContainer {
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Android Library";
|
||||
return mName;
|
||||
}
|
||||
|
||||
public int getKind() {
|
||||
|
||||
@@ -16,10 +16,16 @@
|
||||
|
||||
package com.android.ide.eclipse.adt.project.internal;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
@@ -38,10 +44,6 @@ import org.eclipse.jdt.core.JavaModelException;
|
||||
* {@link IProject}s. This removes the hard-coded path to the android.jar.
|
||||
*/
|
||||
public class AndroidClasspathContainerInitializer extends ClasspathContainerInitializer {
|
||||
/** The old container id */
|
||||
private final static String OLD_CONTAINER_ID =
|
||||
"com.android.ide.eclipse.adt.project.AndroidClasspathContainerInitializer"; //$NON-NLS-1$
|
||||
|
||||
/** The container id for the android framework jar file */
|
||||
private final static String CONTAINER_ID =
|
||||
"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$
|
||||
@@ -58,17 +60,10 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
*/
|
||||
@Override
|
||||
public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
|
||||
String id = null;
|
||||
if (OLD_CONTAINER_ID.equals(containerPath.toString())) {
|
||||
id = OLD_CONTAINER_ID;
|
||||
} else if (CONTAINER_ID.equals(containerPath.toString())) {
|
||||
id = CONTAINER_ID;
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
JavaCore.setClasspathContainer(new Path(id),
|
||||
if (CONTAINER_ID.equals(containerPath.toString())) {
|
||||
JavaCore.setClasspathContainer(new Path(CONTAINER_ID),
|
||||
new IJavaProject[] { project },
|
||||
new IClasspathContainer[] { allocateAndroidContainer(id) },
|
||||
new IClasspathContainer[] { allocateAndroidContainer(CONTAINER_ID, project) },
|
||||
new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
@@ -81,15 +76,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
return JavaCore.newContainerEntry(new Path(CONTAINER_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the {@link IPath} objects against the old android framework container id and
|
||||
* returns <code>true</code> if they are identical.
|
||||
* @param path the <code>IPath</code> to check.
|
||||
*/
|
||||
public static boolean checkOldPath(IPath path) {
|
||||
return OLD_CONTAINER_ID.equals(path.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the {@link IPath} objects against the android framework container id and
|
||||
* returns <code>true</code> if they are identical.
|
||||
@@ -106,41 +92,18 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
* @return <code>true</code> if success, <code>false</code> otherwise.
|
||||
*/
|
||||
public static boolean updateProjects(IJavaProject[] androidProjects) {
|
||||
|
||||
try {
|
||||
// because those projects could have the old id, we are going to fix
|
||||
// them dynamically here.
|
||||
for (IJavaProject javaProject: androidProjects) {
|
||||
IClasspathEntry[] entries = javaProject.getRawClasspath();
|
||||
|
||||
int containerIndex = ProjectHelper.findClasspathEntryByPath(entries,
|
||||
OLD_CONTAINER_ID,
|
||||
IClasspathEntry.CPE_CONTAINER);
|
||||
if (containerIndex != -1) {
|
||||
// the project has the old container, we remove it
|
||||
entries = ProjectHelper.removeEntryFromClasspath(entries, containerIndex);
|
||||
|
||||
// we add the new one instead
|
||||
entries = ProjectHelper.addEntryToClasspath(entries, getContainerEntry());
|
||||
|
||||
// and give the new entries to the project
|
||||
javaProject.setRawClasspath(entries, new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new AndroidClasspathContainer, and associate it to the android framework
|
||||
// container id for each projects.
|
||||
// By providing a new association between a container id and a IClasspathContainer,
|
||||
// this forces the JDT to query the IClasspathContainer for new IClasspathEntry (with
|
||||
// IClasspathContainer#getClasspathEntries()), and therefore force recompilation of
|
||||
// the projects.
|
||||
// TODO: We could only do that for the projects haven't fixed above
|
||||
// (this isn't something that will happen a lot though)
|
||||
int projectCount = androidProjects.length;
|
||||
|
||||
IClasspathContainer[] containers = new IClasspathContainer[projectCount];
|
||||
for (int i = 0 ; i < projectCount; i++) {
|
||||
containers[i] = allocateAndroidContainer(CONTAINER_ID);
|
||||
containers[i] = allocateAndroidContainer(CONTAINER_ID, androidProjects[i]);
|
||||
}
|
||||
|
||||
// give each project their new container in one call.
|
||||
@@ -158,23 +121,123 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
* Allocates and returns an {@link AndroidClasspathContainer} object with the proper
|
||||
* path to the framework jar file.
|
||||
* @param containerId the container id to be used.
|
||||
* @param javaProject The java project that will receive the container.
|
||||
*/
|
||||
private static IClasspathContainer allocateAndroidContainer(String containerId) {
|
||||
return new AndroidClasspathContainer(createFrameworkClasspath(), new Path(containerId));
|
||||
private static IClasspathContainer allocateAndroidContainer(String containerId,
|
||||
IJavaProject javaProject) {
|
||||
IProject iProject = javaProject.getProject();
|
||||
|
||||
// remove potential MARKER_TARGETs.
|
||||
try {
|
||||
if (iProject.exists()) {
|
||||
iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
|
||||
IResource.DEPTH_INFINITE);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
// just log the error
|
||||
AdtPlugin.log(ce, "Error removing target marker.");
|
||||
}
|
||||
|
||||
|
||||
// first we check if the SDK has been loaded
|
||||
boolean sdkIsLoaded = AdtPlugin.getDefault().getSdkLoadStatus(javaProject) ==
|
||||
LoadStatus.LOADED;
|
||||
|
||||
// then we check if the project has a valid target.
|
||||
IAndroidTarget target = null;
|
||||
if (sdkIsLoaded) {
|
||||
target = Sdk.getCurrent().getTarget(iProject);
|
||||
}
|
||||
|
||||
// if we are loaded and the target is non null, we create a valid ClassPathContainer
|
||||
if (sdkIsLoaded && target != null) {
|
||||
String targetName = null;
|
||||
if (target.isPlatform()) {
|
||||
targetName = target.getName();
|
||||
} else {
|
||||
targetName = String.format("%1$s (%2$s)", target.getName(),
|
||||
target.getApiVersionName());
|
||||
}
|
||||
|
||||
return new AndroidClasspathContainer(createFrameworkClasspath(target),
|
||||
new Path(containerId), targetName);
|
||||
}
|
||||
|
||||
// else we put a marker on the project, and return a dummy container (to replace the
|
||||
// previous one if there was one.)
|
||||
|
||||
// Get the project's target's hash string (if it exists)
|
||||
String hashString = Sdk.getProjectTargetHashString(iProject);
|
||||
|
||||
String message = null;
|
||||
boolean outputToConsole = true;
|
||||
if (hashString == null || hashString.length() == 0) {
|
||||
message = String.format(
|
||||
"Project has no target set. Edit the project properties to set one.");
|
||||
} else if (sdkIsLoaded) {
|
||||
message = String.format(
|
||||
"Unable to resolve target '%s'", hashString);
|
||||
} else {
|
||||
// this is the case where there is a hashString but the SDK is not yet
|
||||
// loaded and therefore we can't get the target yet.
|
||||
message = String.format(
|
||||
"Unable to resolve target '%s' until the SDK is loaded.", hashString);
|
||||
|
||||
// let's not log this one to the console as it will happen at every boot,
|
||||
// and it's expected. (we do keep the error marker though).
|
||||
outputToConsole = false;
|
||||
}
|
||||
|
||||
// log the error and put the marker on the project
|
||||
if (outputToConsole) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, message);
|
||||
}
|
||||
IMarker marker = BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, message,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
|
||||
// add a marker priority as this is an more important error than the error that will
|
||||
// spring from the lack of library
|
||||
try {
|
||||
marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
|
||||
} catch (CoreException e) {
|
||||
// just log the error
|
||||
AdtPlugin.log(e, "Error changing target marker priority.");
|
||||
}
|
||||
|
||||
// return a dummy container to replace the one we may have had before.
|
||||
return new IClasspathContainer() {
|
||||
public IClasspathEntry[] getClasspathEntries() {
|
||||
return new IClasspathEntry[0];
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Unable to get system library for the project";
|
||||
}
|
||||
|
||||
public int getKind() {
|
||||
return IClasspathContainer.K_DEFAULT_SYSTEM;
|
||||
}
|
||||
|
||||
public IPath getPath() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link IClasspathEntry} object for the android framework.
|
||||
* <p/>This references the OS path to the android.jar and the java doc directory. This is
|
||||
* dynamically created when a project is opened, and never saved in the project itself, so
|
||||
* there's no risk of storing an obsolete path.
|
||||
* Creates and returns a new {@link IClasspathEntry} object for the android
|
||||
* framework. <p/>This references the OS path to the android.jar and the
|
||||
* java doc directory. This is dynamically created when a project is opened,
|
||||
* and never saved in the project itself, so there's no risk of storing an
|
||||
* obsolete path.
|
||||
*
|
||||
* @param target The target that contains the libraries.
|
||||
*/
|
||||
private static IClasspathEntry createFrameworkClasspath() {
|
||||
private static IClasspathEntry createFrameworkClasspath(IAndroidTarget target) {
|
||||
// now add the android framework to the class path.
|
||||
// create the path object.
|
||||
IPath android_lib = new Path(AdtPlugin.getOsAbsoluteFramework());
|
||||
|
||||
IPath android_src = new Path(AdtPlugin.getOsAbsoluteAndroidSources());
|
||||
IPath android_lib = new Path(target.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
IPath android_src = new Path(target.getPath(IAndroidTarget.SOURCES));
|
||||
|
||||
// create the java doc link.
|
||||
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.project.properties;
|
||||
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdkuilib.SdkTargetSelector;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.ui.IWorkbenchPropertyPage;
|
||||
import org.eclipse.ui.dialogs.PropertyPage;
|
||||
|
||||
/**
|
||||
* Property page for "Android" project.
|
||||
* This is accessible from the Package Explorer when right clicking a project and choosing
|
||||
* "Properties".
|
||||
*
|
||||
*/
|
||||
public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPropertyPage {
|
||||
|
||||
private IProject mProject;
|
||||
private SdkTargetSelector mSelector;
|
||||
|
||||
public AndroidPropertyPage() {
|
||||
// pass
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createContents(Composite parent) {
|
||||
// get the element (this is not yet valid in the constructor).
|
||||
mProject = (IProject)getElement();
|
||||
|
||||
Composite top = new Composite(parent, SWT.NONE);
|
||||
top.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
top.setLayout(new GridLayout(1, false));
|
||||
|
||||
Label l = new Label(top, SWT.NONE);
|
||||
l.setText("Project Target");
|
||||
|
||||
// get the targets from the sdk
|
||||
IAndroidTarget[] targets = null;
|
||||
if (Sdk.getCurrent() != null) {
|
||||
targets = Sdk.getCurrent().getTargets();
|
||||
}
|
||||
|
||||
// build the UI.
|
||||
mSelector = new SdkTargetSelector(top, targets, false /*allowMultipleSelection*/);
|
||||
|
||||
if (Sdk.getCurrent() != null) {
|
||||
IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
|
||||
if (target != null) {
|
||||
mSelector.setSelection(target);
|
||||
}
|
||||
}
|
||||
|
||||
mSelector.setSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
// look for the selection and validate the page if there is a selection
|
||||
IAndroidTarget target = mSelector.getFirstSelected();
|
||||
setValid(target != null);
|
||||
}
|
||||
});
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performOk() {
|
||||
if (Sdk.getCurrent() != null) {
|
||||
Sdk.getCurrent().setProject(mProject, mSelector.getFirstSelected());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
@@ -35,9 +34,12 @@ import javax.management.InvalidAttributeValueException;
|
||||
/**
|
||||
* Custom class loader able to load a class from the SDK jar file.
|
||||
*/
|
||||
public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader {
|
||||
|
||||
public final static class ClassWrapper implements IClass {
|
||||
/**
|
||||
* Wrapper around a {@link Class} to provide the methods of {@link IClassDescriptor}.
|
||||
*/
|
||||
public final static class ClassWrapper implements IClassDescriptor {
|
||||
private Class<?> mClass;
|
||||
|
||||
public ClassWrapper(Class<?> clazz) {
|
||||
@@ -48,9 +50,9 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
return mClass.getCanonicalName();
|
||||
}
|
||||
|
||||
public IClass[] getDeclaredClasses() {
|
||||
public IClassDescriptor[] getDeclaredClasses() {
|
||||
Class<?>[] classes = mClass.getDeclaredClasses();
|
||||
IClass[] iclasses = new IClass[classes.length];
|
||||
IClassDescriptor[] iclasses = new IClassDescriptor[classes.length];
|
||||
for (int i = 0 ; i < classes.length ; i++) {
|
||||
iclasses[i] = new ClassWrapper(classes[i]);
|
||||
}
|
||||
@@ -58,7 +60,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
return iclasses;
|
||||
}
|
||||
|
||||
public IClass getEnclosingClass() {
|
||||
public IClassDescriptor getEnclosingClass() {
|
||||
return new ClassWrapper(mClass.getEnclosingClass());
|
||||
}
|
||||
|
||||
@@ -66,7 +68,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
return mClass.getSimpleName();
|
||||
}
|
||||
|
||||
public IClass getSuperclass() {
|
||||
public IClassDescriptor getSuperclass() {
|
||||
return new ClassWrapper(mClass.getSuperclass());
|
||||
}
|
||||
|
||||
@@ -131,17 +133,18 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
* @param packageFilter The package that contains all the class data to preload, using a fully
|
||||
* qualified binary name (.e.g "com.my.package."). The matching algorithm
|
||||
* is simple "startsWith". Use an empty string to include everything.
|
||||
* @param taskLabel An optional task name for the sub monitor. Can be null.
|
||||
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
|
||||
* @throws IOException
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public void preLoadClasses(String packageFilter, IProgressMonitor monitor)
|
||||
public void preLoadClasses(String packageFilter, String taskLabel, IProgressMonitor monitor)
|
||||
throws IOException, InvalidAttributeValueException, ClassFormatError {
|
||||
// Transform the package name into a zip entry path
|
||||
String pathFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
SubMonitor progress = SubMonitor.convert(monitor, 100);
|
||||
SubMonitor progress = SubMonitor.convert(monitor, taskLabel == null ? "" : taskLabel, 100);
|
||||
|
||||
// create streams to read the intermediary archive
|
||||
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
|
||||
@@ -174,6 +177,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
// advance 5% of whatever is allocated on the progress bar
|
||||
progress.setWorkRemaining(100);
|
||||
progress.worked(5);
|
||||
progress.subTask(String.format("Preload %1$s", className));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,17 +197,18 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(
|
||||
public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(
|
||||
String packageFilter,
|
||||
String[] superClasses)
|
||||
throws IOException, InvalidAttributeValueException, ClassFormatError {
|
||||
|
||||
packageFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
HashMap<String, ArrayList<IClass>> mClassesFound = new HashMap<String, ArrayList<IClass>>();
|
||||
HashMap<String, ArrayList<IClassDescriptor>> mClassesFound =
|
||||
new HashMap<String, ArrayList<IClassDescriptor>>();
|
||||
|
||||
for (String className : superClasses) {
|
||||
mClassesFound.put(className, new ArrayList<IClass>());
|
||||
mClassesFound.put(className, new ArrayList<IClassDescriptor>());
|
||||
}
|
||||
|
||||
// create streams to read the intermediary archive
|
||||
@@ -415,7 +420,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader {
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException {
|
||||
public IClassDescriptor getClass(String className) throws ClassNotFoundException {
|
||||
try {
|
||||
return new ClassWrapper(loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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.sdk;
|
||||
|
||||
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
||||
import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors;
|
||||
import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
|
||||
import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors;
|
||||
import com.android.layoutlib.api.ILayoutBridge;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class contains the data of an Android Target as loaded from the SDK.
|
||||
*/
|
||||
public class AndroidTargetData {
|
||||
|
||||
public final static int DESCRIPTOR_MANIFEST = 1;
|
||||
public final static int DESCRIPTOR_LAYOUT = 2;
|
||||
public final static int DESCRIPTOR_MENU = 3;
|
||||
public final static int DESCRIPTOR_XML = 4;
|
||||
public final static int DESCRIPTOR_RESOURCES = 5;
|
||||
public final static int DESCRIPTOR_SEARCHABLE = 6;
|
||||
public final static int DESCRIPTOR_PREFERENCES = 7;
|
||||
|
||||
public final static class LayoutBridge {
|
||||
/** Link to the layout bridge */
|
||||
public ILayoutBridge bridge;
|
||||
|
||||
public LoadStatus status = LoadStatus.LOADING;
|
||||
|
||||
public ClassLoader classLoader;
|
||||
}
|
||||
|
||||
private final IAndroidTarget mTarget;
|
||||
|
||||
/**
|
||||
* mAttributeValues is a map { key => list [ values ] }.
|
||||
* The key for the map is "(element-xml-name,attribute-namespace:attribute-xml-local-name)".
|
||||
* The attribute namespace prefix must be:
|
||||
* - "android" for AndroidConstants.NS_RESOURCES
|
||||
* - "xmlns" for the XMLNS URI.
|
||||
*
|
||||
* This is used for attributes that do not have a unique name, but still need to be populated
|
||||
* with values in the UI. Uniquely named attributes have their values in {@link #mEnumValueMap}.
|
||||
*/
|
||||
private final Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>();
|
||||
|
||||
private IResourceRepository mSystemResourceRepository;
|
||||
|
||||
private final AndroidManifestDescriptors mManifestDescriptors;
|
||||
private final LayoutDescriptors mLayoutDescriptors;
|
||||
private final MenuDescriptors mMenuDescriptors;
|
||||
private final XmlDescriptors mXmlDescriptors;
|
||||
|
||||
private final Map<String, Map<String, Integer>> mEnumValueMap;
|
||||
|
||||
private final ProjectResources mFrameworkResources;
|
||||
private final LayoutBridge mLayoutBridge;
|
||||
|
||||
private boolean mLayoutBridgeInit = false;
|
||||
|
||||
/**
|
||||
* Creates an AndroidTargetData object.
|
||||
*/
|
||||
AndroidTargetData(IAndroidTarget androidTarget,
|
||||
IResourceRepository systemResourceRepository,
|
||||
AndroidManifestDescriptors manifestDescriptors,
|
||||
LayoutDescriptors layoutDescriptors,
|
||||
MenuDescriptors menuDescriptors,
|
||||
XmlDescriptors xmlDescriptors,
|
||||
Map<String, Map<String, Integer>> enumValueMap,
|
||||
String[] permissionValues,
|
||||
String[] activityIntentActionValues,
|
||||
String[] broadcastIntentActionValues,
|
||||
String[] serviceIntentActionValues,
|
||||
String[] intentCategoryValues,
|
||||
ProjectResources resources,
|
||||
LayoutBridge layoutBridge) {
|
||||
|
||||
mTarget = androidTarget;
|
||||
mSystemResourceRepository = systemResourceRepository;
|
||||
mManifestDescriptors = manifestDescriptors;
|
||||
mLayoutDescriptors = layoutDescriptors;
|
||||
mMenuDescriptors = menuDescriptors;
|
||||
mXmlDescriptors = xmlDescriptors;
|
||||
mEnumValueMap = enumValueMap;
|
||||
mFrameworkResources = resources;
|
||||
mLayoutBridge = layoutBridge;
|
||||
|
||||
setPermissions(permissionValues);
|
||||
setIntentFilterActionsAndCategories(activityIntentActionValues, broadcastIntentActionValues,
|
||||
serviceIntentActionValues, intentCategoryValues);
|
||||
}
|
||||
|
||||
public IResourceRepository getSystemResources() {
|
||||
return mSystemResourceRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link IDescriptorProvider} from a given Id.
|
||||
* The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT},
|
||||
* {@link #DESCRIPTOR_MENU}, or {@link #DESCRIPTOR_XML}.
|
||||
* All other values will throw an {@link IllegalArgumentException}.
|
||||
*/
|
||||
public IDescriptorProvider getDescriptorProvider(int descriptorId) {
|
||||
switch (descriptorId) {
|
||||
case DESCRIPTOR_MANIFEST:
|
||||
return mManifestDescriptors;
|
||||
case DESCRIPTOR_LAYOUT:
|
||||
return mLayoutDescriptors;
|
||||
case DESCRIPTOR_MENU:
|
||||
return mMenuDescriptors;
|
||||
case DESCRIPTOR_XML:
|
||||
return mXmlDescriptors;
|
||||
case DESCRIPTOR_RESOURCES:
|
||||
// FIXME: since it's hard-coded the Resources Descriptors are not platform dependent.
|
||||
return ResourcesDescriptors.getInstance();
|
||||
case DESCRIPTOR_PREFERENCES:
|
||||
return mXmlDescriptors.getPreferencesProvider();
|
||||
case DESCRIPTOR_SEARCHABLE:
|
||||
return mXmlDescriptors.getSearchableProvider();
|
||||
default :
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the manifest descriptors.
|
||||
*/
|
||||
public AndroidManifestDescriptors getManifestDescriptors() {
|
||||
return mManifestDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout Descriptors.
|
||||
*/
|
||||
public LayoutDescriptors getLayoutDescriptors() {
|
||||
return mLayoutDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the menu descriptors.
|
||||
*/
|
||||
public MenuDescriptors getMenuDescriptors() {
|
||||
return mMenuDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML descriptors
|
||||
*/
|
||||
public XmlDescriptors getXmlDescriptors() {
|
||||
return mXmlDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this list of possible values for an XML attribute.
|
||||
* <p/>This should only be called for attributes for which possible values depend on the
|
||||
* parent element node.
|
||||
* <p/>For attributes that have the same values no matter the parent node, use
|
||||
* {@link #getEnumValueMap()}.
|
||||
* @param elementName the name of the element containing the attribute.
|
||||
* @param attributeName the name of the attribute
|
||||
* @return an array of String with the possible values, or <code>null</code> if no values were
|
||||
* found.
|
||||
*/
|
||||
public String[] getAttributeValues(String elementName, String attributeName) {
|
||||
String key = String.format("(%1$s,%2$s)", elementName, attributeName); //$NON-NLS-1$
|
||||
return mAttributeValues.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this list of possible values for an XML attribute.
|
||||
* <p/>This should only be called for attributes for which possible values depend on the
|
||||
* parent and great-grand-parent element node.
|
||||
* <p/>The typical example of this is for the 'name' attribute under
|
||||
* activity/intent-filter/action
|
||||
* <p/>For attributes that have the same values no matter the parent node, use
|
||||
* {@link #getEnumValueMap()}.
|
||||
* @param elementName the name of the element containing the attribute.
|
||||
* @param attributeName the name of the attribute
|
||||
* @param greatGrandParentElementName the great-grand-parent node.
|
||||
* @return an array of String with the possible values, or <code>null</code> if no values were
|
||||
* found.
|
||||
*/
|
||||
public String[] getAttributeValues(String elementName, String attributeName,
|
||||
String greatGrandParentElementName) {
|
||||
if (greatGrandParentElementName != null) {
|
||||
String key = String.format("(%1$s,%2$s,%3$s)", //$NON-NLS-1$
|
||||
greatGrandParentElementName, elementName, attributeName);
|
||||
String[] values = mAttributeValues.get(key);
|
||||
if (values != null) {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
return getAttributeValues(elementName, attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enum values map.
|
||||
* <p/>The map defines the possible values for XML attributes. The key is the attribute name
|
||||
* and the value is a map of (string, integer) in which the key (string) is the name of
|
||||
* the value, and the Integer is the numerical value in the compiled binary XML files.
|
||||
*/
|
||||
public Map<String, Map<String, Integer>> getEnumValueMap() {
|
||||
return mEnumValueMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ProjectResources} containing the Framework Resources.
|
||||
*/
|
||||
public ProjectResources getFrameworkResources() {
|
||||
return mFrameworkResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object.
|
||||
* <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will
|
||||
* contain the reason (either {@link LoadStatus#LOADING} or {@link LoadStatus#FAILED}).
|
||||
* <p/>Valid {@link ILayoutBridge} objects are always initialized before being returned.
|
||||
*/
|
||||
public synchronized LayoutBridge getLayoutBridge() {
|
||||
if (mLayoutBridgeInit == false && mLayoutBridge.bridge != null) {
|
||||
mLayoutBridge.bridge.init(mTarget.getPath(IAndroidTarget.FONTS),
|
||||
getEnumValueMap());
|
||||
mLayoutBridgeInit = true;
|
||||
}
|
||||
return mLayoutBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission values
|
||||
* @param permissionValues the list of permissions
|
||||
*/
|
||||
private void setPermissions(String[] permissionValues) {
|
||||
setValues("(uses-permission,android:name)", permissionValues); //$NON-NLS-1$
|
||||
setValues("(application,android:permission)", permissionValues); //$NON-NLS-1$
|
||||
setValues("(activity,android:permission)", permissionValues); //$NON-NLS-1$
|
||||
setValues("(receiver,android:permission)", permissionValues); //$NON-NLS-1$
|
||||
setValues("(service,android:permission)", permissionValues); //$NON-NLS-1$
|
||||
setValues("(provider,android:permission)", permissionValues); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
private void setIntentFilterActionsAndCategories(String[] activityIntentActions,
|
||||
String[] broadcastIntentActions, String[] serviceIntentActions,
|
||||
String[] intentCategoryValues) {
|
||||
setValues("(activity,action,android:name)", activityIntentActions); //$NON-NLS-1$
|
||||
setValues("(receiver,action,android:name)", broadcastIntentActions); //$NON-NLS-1$
|
||||
setValues("(service,action,android:name)", serviceIntentActions); //$NON-NLS-1$
|
||||
setValues("(category,android:name)", intentCategoryValues); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a (name, values) pair in the hash map.
|
||||
* <p/>
|
||||
* If the name is already present in the map, it is first removed.
|
||||
* @param name the name associated with the values.
|
||||
* @param values The values to add.
|
||||
*/
|
||||
private void setValues(String name, String[] values) {
|
||||
mAttributeValues.remove(name);
|
||||
mAttributeValues.put(name, values);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,19 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.AttrsXmlParser;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo;
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
||||
import com.android.ide.eclipse.common.resources.ResourceItem;
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors;
|
||||
import com.android.layoutlib.api.ILayoutBridge;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -34,10 +44,11 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -48,7 +59,7 @@ import java.util.Map;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
|
||||
/**
|
||||
* Parser for the framework library.
|
||||
* Parser for the platform data in an SDK.
|
||||
* <p/>
|
||||
* This gather the following information:
|
||||
* <ul>
|
||||
@@ -57,14 +68,16 @@ import javax.management.InvalidAttributeValueException;
|
||||
* <li></li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class FrameworkResourceParser {
|
||||
public final class AndroidTargetParser {
|
||||
|
||||
private static final String TAG = "Framework Resource Parser";
|
||||
private final IAndroidTarget mAndroidTarget;
|
||||
|
||||
/**
|
||||
* Creates a framework resource parser.
|
||||
* Creates a platform data parser.
|
||||
*/
|
||||
public FrameworkResourceParser() {
|
||||
public AndroidTargetParser(IAndroidTarget platformTarget) {
|
||||
mAndroidTarget = platformTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,34 +90,29 @@ public final class FrameworkResourceParser {
|
||||
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
|
||||
* @return True if the SDK path was valid and parsing has been attempted.
|
||||
*/
|
||||
public boolean parse(String osSdkPath, FrameworkResourceManager resourceManager,
|
||||
IProgressMonitor monitor) {
|
||||
if (osSdkPath == null || osSdkPath.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
SubMonitor progress = SubMonitor.convert(monitor, 100);
|
||||
SubMonitor progress = SubMonitor.convert(monitor,
|
||||
String.format("Parsing SDK %1$s", mAndroidTarget.getName()),
|
||||
120);
|
||||
|
||||
AndroidJarLoader classLoader =
|
||||
new AndroidJarLoader(osSdkPath + AndroidConstants.FN_FRAMEWORK_LIBRARY);
|
||||
new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
|
||||
progress.subTask("Preloading");
|
||||
preload(classLoader, progress.newChild(40));
|
||||
progress.setWorkRemaining(60);
|
||||
preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE));
|
||||
progress.setWorkRemaining(80);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
// get the resource Ids.
|
||||
progress.subTask("Resource IDs");
|
||||
FrameworkResourceRepository systemResourceRepository = new FrameworkResourceRepository(
|
||||
collectResourceIds(classLoader));
|
||||
IResourceRepository frameworkRepository = collectResourceIds(classLoader);
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
// get the permissions
|
||||
@@ -113,56 +121,59 @@ public final class FrameworkResourceParser {
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
String osLibPath = osSdkPath + AndroidConstants.OS_SDK_LIBS_FOLDER;
|
||||
|
||||
// get the action and category values for the Intents.
|
||||
progress.subTask("Intents");
|
||||
ArrayList<String> activity_actions = new ArrayList<String>();
|
||||
ArrayList<String> broadcast_actions = new ArrayList<String>();
|
||||
ArrayList<String> service_actions = new ArrayList<String>();
|
||||
ArrayList<String> categories = new ArrayList<String>();
|
||||
collectIntentFilterActionsAndCategories(osLibPath,
|
||||
activity_actions, broadcast_actions, service_actions, categories);
|
||||
collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions,
|
||||
service_actions, categories);
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
progress.subTask("Layouts");
|
||||
// gather the attribute definition
|
||||
progress.subTask("Attributes definitions");
|
||||
AttrsXmlParser attrsXmlParser = new AttrsXmlParser(
|
||||
osSdkPath + AndroidConstants.OS_SDK_ATTRS_XML);
|
||||
mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES));
|
||||
attrsXmlParser.preload();
|
||||
|
||||
progress.subTask("Manifest definitions");
|
||||
AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser(
|
||||
osSdkPath + AndroidConstants.OS_SDK_ATTRS_MANIFEST_XML,
|
||||
mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES),
|
||||
attrsXmlParser);
|
||||
attrsManifestXmlParser.preload();
|
||||
|
||||
Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>();
|
||||
Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>();
|
||||
|
||||
collectLayoutClasses(osLibPath, classLoader, attrsXmlParser, mainList, groupList,
|
||||
// collect the layout/widgets classes
|
||||
progress.subTask("Widgets and layouts");
|
||||
collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList,
|
||||
progress.newChild(40));
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]);
|
||||
ViewClassInfo[] layoutGroupsInfo = groupList.toArray(
|
||||
new ViewClassInfo[groupList.size()]);
|
||||
|
||||
// collect the preferences classes.
|
||||
mainList.clear();
|
||||
groupList.clear();
|
||||
collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList,
|
||||
progress.newChild(5));
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]);
|
||||
@@ -177,34 +188,77 @@ public final class FrameworkResourceParser {
|
||||
Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues();
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
String docBaseUrl = getDocumentationBaseUrl(
|
||||
osSdkPath + AndroidConstants.OS_SDK_DOCS_FOLDER);
|
||||
// From the information that was collected, create the pieces that will be put in
|
||||
// the PlatformData object.
|
||||
AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors();
|
||||
manifestDescriptors.updateDescriptors(manifestMap);
|
||||
progress.worked(10);
|
||||
|
||||
FrameworkResourceManager.getInstance().setResources(systemResourceRepository,
|
||||
layoutViewsInfo,
|
||||
layoutGroupsInfo,
|
||||
preferencesInfo,
|
||||
preferenceGroupsInfo,
|
||||
xmlMenuMap,
|
||||
xmlSearchableMap,
|
||||
manifestMap,
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
LayoutDescriptors layoutDescriptors = new LayoutDescriptors();
|
||||
layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo);
|
||||
progress.worked(10);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
MenuDescriptors menuDescriptors = new MenuDescriptors();
|
||||
menuDescriptors.updateDescriptors(xmlMenuMap);
|
||||
progress.worked(10);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
XmlDescriptors xmlDescriptors = new XmlDescriptors();
|
||||
xmlDescriptors.updateDescriptors(xmlSearchableMap, preferencesInfo,
|
||||
preferenceGroupsInfo);
|
||||
progress.worked(10);
|
||||
|
||||
// load the framework resources.
|
||||
ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources(
|
||||
mAndroidTarget);
|
||||
progress.worked(10);
|
||||
|
||||
// now load the layout lib bridge
|
||||
LayoutBridge layoutBridge = loadLayoutBridge();
|
||||
progress.worked(10);
|
||||
|
||||
// and finally create the PlatformData with all that we loaded.
|
||||
AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget,
|
||||
frameworkRepository,
|
||||
manifestDescriptors,
|
||||
layoutDescriptors,
|
||||
menuDescriptors,
|
||||
xmlDescriptors,
|
||||
enumValueMap,
|
||||
permissionValues,
|
||||
activity_actions.toArray(new String[activity_actions.size()]),
|
||||
broadcast_actions.toArray(new String[broadcast_actions.size()]),
|
||||
service_actions.toArray(new String[service_actions.size()]),
|
||||
categories.toArray(new String[categories.size()]),
|
||||
docBaseUrl);
|
||||
resources,
|
||||
layoutBridge);
|
||||
|
||||
Sdk.getCurrent().setTargetData(mAndroidTarget, targetData);
|
||||
|
||||
return true;
|
||||
return Status.OK_STATUS;
|
||||
} catch (Exception e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$
|
||||
AdtPlugin.printToConsole("SDK parser failed", e.getMessage());
|
||||
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e);
|
||||
} finally {
|
||||
if (monitor != null) {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +271,9 @@ public final class FrameworkResourceParser {
|
||||
*/
|
||||
private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) {
|
||||
try {
|
||||
classLoader.preLoadClasses("" /* all classes */, monitor); //$NON-NLS-1$
|
||||
classLoader.preLoadClasses("" /* all classes */, //$NON-NLS-1$
|
||||
mAndroidTarget.getName(), // monitor task label
|
||||
monitor);
|
||||
} catch (InvalidAttributeValueException e) {
|
||||
AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
@@ -226,24 +282,27 @@ public final class FrameworkResourceParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the resources IDs found in the SDK.
|
||||
* Creates an IResourceRepository for the framework resources.
|
||||
*
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @return a map of the resources, or null if it failed.
|
||||
*/
|
||||
private Map<ResourceType, List<ResourceItem>> collectResourceIds(
|
||||
private IResourceRepository collectResourceIds(
|
||||
AndroidJarLoader classLoader) {
|
||||
try {
|
||||
Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R);
|
||||
|
||||
if (r != null) {
|
||||
return parseRClass(r);
|
||||
Map<ResourceType, List<ResourceItem>> map = parseRClass(r);
|
||||
if (map != null) {
|
||||
return new FrameworkResourceRepository(map);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG,
|
||||
"Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$
|
||||
AndroidConstants.CLASS_R,
|
||||
classLoader.getSource());
|
||||
mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -331,7 +390,7 @@ public final class FrameworkResourceParser {
|
||||
AdtPlugin.logAndPrintError(e, TAG,
|
||||
"Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$
|
||||
AndroidConstants.CLASS_MANIFEST_PERMISSION,
|
||||
classLoader.getSource());
|
||||
mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
@@ -347,13 +406,17 @@ public final class FrameworkResourceParser {
|
||||
* @param serviceActions the list which will receive the service action values.
|
||||
* @param categories the list which will receive the category values.
|
||||
*/
|
||||
private void collectIntentFilterActionsAndCategories(String osLibPath,
|
||||
ArrayList<String> activityActions, ArrayList<String> broadcastActions,
|
||||
private void collectIntentFilterActionsAndCategories(ArrayList<String> activityActions,
|
||||
ArrayList<String> broadcastActions,
|
||||
ArrayList<String> serviceActions, ArrayList<String> categories) {
|
||||
collectValues(osLibPath + "activity_actions.txt" , activityActions);
|
||||
collectValues(osLibPath + "broadcast_actions.txt" , broadcastActions);
|
||||
collectValues(osLibPath + "service_actions.txt" , serviceActions);
|
||||
collectValues(osLibPath + "categories.txt" , categories);
|
||||
collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_ACTIVITY),
|
||||
activityActions);
|
||||
collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_BROADCAST),
|
||||
broadcastActions);
|
||||
collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_SERVICE),
|
||||
serviceActions);
|
||||
collectValues(mAndroidTarget.getPath(IAndroidTarget.CATEGORIES),
|
||||
categories);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -400,21 +463,21 @@ public final class FrameworkResourceParser {
|
||||
* Collects all layout classes information from the class loader and the
|
||||
* attrs.xml and sets the corresponding structures in the resource manager.
|
||||
*
|
||||
* @param osLibPath The OS path to the SDK tools/lib folder, ending with a separator.
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @param classLoader The framework SDK jar classloader in case we cannot get the widget from
|
||||
* the platform directly
|
||||
* @param attrsXmlParser The parser of the attrs.xml file
|
||||
* @param mainList the Collection to receive the main list of {@link ViewClassInfo}.
|
||||
* @param groupList the Collection to receive the group list of {@link ViewClassInfo}.
|
||||
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
|
||||
*/
|
||||
private void collectLayoutClasses(String osLibPath,
|
||||
AndroidJarLoader classLoader,
|
||||
private void collectLayoutClasses(AndroidJarLoader classLoader,
|
||||
AttrsXmlParser attrsXmlParser,
|
||||
Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList,
|
||||
IProgressMonitor monitor) {
|
||||
LayoutParamsParser ldp = null;
|
||||
try {
|
||||
WidgetListLoader loader = new WidgetListLoader(osLibPath + "widgets.txt");
|
||||
WidgetClassLoader loader = new WidgetClassLoader(
|
||||
mAndroidTarget.getPath(IAndroidTarget.WIDGETS));
|
||||
if (loader.parseWidgetList(monitor)) {
|
||||
ldp = new LayoutParamsParser(loader, attrsXmlParser);
|
||||
}
|
||||
@@ -465,12 +528,13 @@ public final class FrameworkResourceParser {
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG,
|
||||
"Collect preferences failed, class %1$s not found in %2$s", //$NON-NLS-1$
|
||||
"Collect preferences failed, class %1$s not found in %2$s",
|
||||
e.getMessage(),
|
||||
classLoader.getSource());
|
||||
} catch (Throwable e) {
|
||||
AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$
|
||||
AdtPlugin.printErrorToConsole("Android Framework Parser", "failed to collect preference classes");
|
||||
AdtPlugin.printErrorToConsole("Android Framework Parser",
|
||||
"failed to collect preference classes");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,40 +601,51 @@ public final class FrameworkResourceParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the local documentation.
|
||||
* Can return null if no documentation is found in the current SDK.
|
||||
*
|
||||
* @param osDocsPath Path to the documentation folder in the current SDK.
|
||||
* The folder may not actually exist.
|
||||
* @return A file:// URL on the local documentation folder if it exists or null.
|
||||
* Loads the layout bridge from the dynamically loaded layoutlib.jar
|
||||
*/
|
||||
private String getDocumentationBaseUrl(String osDocsPath) {
|
||||
File f = new File(osDocsPath);
|
||||
private LayoutBridge loadLayoutBridge() {
|
||||
LayoutBridge layoutBridge = new LayoutBridge();
|
||||
|
||||
if (f.isDirectory()) {
|
||||
try {
|
||||
// Note: to create a file:// URL, one would typically use something like
|
||||
// f.toURI().toURL().toString(). However this generates a broken path on
|
||||
// Windows, namely "C:\\foo" is converted to "file:/C:/foo" instead of
|
||||
// "file:///C:/foo" (i.e. there should be 3 / after "file:"). So we'll
|
||||
// do the correct thing manually.
|
||||
try {
|
||||
// get the URL for the file.
|
||||
File f = new File(mAndroidTarget.getPath(IAndroidTarget.LAYOUT_LIB));
|
||||
if (f.isFile() == false) {
|
||||
AdtPlugin.log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$
|
||||
} else {
|
||||
URL url = f.toURL();
|
||||
|
||||
String path = f.getAbsolutePath();
|
||||
if (File.separatorChar != '/') {
|
||||
path = path.replace(File.separatorChar, '/');
|
||||
// create a class loader. Because this jar reference interfaces
|
||||
// that are in the editors plugin, it's important to provide
|
||||
// a parent class loader.
|
||||
layoutBridge.classLoader = new URLClassLoader(new URL[] { url },
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
// load the class
|
||||
Class<?> clazz = layoutBridge.classLoader.loadClass(AndroidConstants.CLASS_BRIDGE);
|
||||
if (clazz != null) {
|
||||
// instantiate an object of the class.
|
||||
Constructor<?> constructor = clazz.getConstructor();
|
||||
if (constructor != null) {
|
||||
Object bridge = constructor.newInstance();
|
||||
if (bridge instanceof ILayoutBridge) {
|
||||
layoutBridge.bridge = (ILayoutBridge)bridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For some reason the URL class doesn't add the mandatory "//" after
|
||||
// the "file:" protocol name, so it has to be hacked into the path.
|
||||
URL url = new URL("file", null, "//" + path); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
String result = url.toString();
|
||||
return result;
|
||||
} catch (MalformedURLException e) {
|
||||
// ignore malformed URLs
|
||||
if (layoutBridge.bridge == null) {
|
||||
layoutBridge.status = LoadStatus.FAILED;
|
||||
AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$
|
||||
} else {
|
||||
layoutBridge.status = LoadStatus.LOADED;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
layoutBridge.status = LoadStatus.FAILED;
|
||||
// log the error.
|
||||
AdtPlugin.log(t, "Failed to load the LayoutLib");
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
return layoutBridge;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
||||
import com.android.ide.eclipse.common.resources.ResourceItem;
|
||||
@@ -26,9 +26,9 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link IResourceRepository} interface to hold the system resource Ids
|
||||
* parsed by {@link FrameworkResourceParser}.
|
||||
* parsed by {@link AndroidTargetParser}.
|
||||
*/
|
||||
public final class FrameworkResourceRepository implements IResourceRepository {
|
||||
final class FrameworkResourceRepository implements IResourceRepository {
|
||||
|
||||
private Map<ResourceType, List<ResourceItem>> mResourcesMap;
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -28,7 +26,25 @@ import javax.management.InvalidAttributeValueException;
|
||||
* Classes which implements this interface provide methods to access framework resource
|
||||
* data loaded from the SDK.
|
||||
*/
|
||||
public interface IAndroidLoader {
|
||||
public interface IAndroidClassLoader {
|
||||
|
||||
/**
|
||||
* Classes which implement this interface provide methods to describe a class.
|
||||
*/
|
||||
public interface IClassDescriptor {
|
||||
|
||||
String getCanonicalName();
|
||||
|
||||
IClassDescriptor getSuperclass();
|
||||
|
||||
String getSimpleName();
|
||||
|
||||
IClassDescriptor getEnclosingClass();
|
||||
|
||||
IClassDescriptor[] getDeclaredClasses();
|
||||
|
||||
boolean isInstantiable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and loads all classes that derive from a given set of super classes.
|
||||
@@ -43,7 +59,7 @@ public interface IAndroidLoader {
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(
|
||||
public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(
|
||||
String rootPackage, String[] superClasses)
|
||||
throws IOException, InvalidAttributeValueException, ClassFormatError;
|
||||
|
||||
@@ -52,7 +68,7 @@ public interface IAndroidLoader {
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException;
|
||||
public IClassDescriptor getClass(String className) throws ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* Returns a string indicating the source of the classes, typically for debugging
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.sdk.IAndroidClassLoader.IClassDescriptor;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.CommonPlugin;
|
||||
import com.android.ide.eclipse.common.resources.AttrsXmlParser;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo;
|
||||
@@ -52,23 +53,10 @@ import javax.management.InvalidAttributeValueException;
|
||||
public class LayoutParamsParser {
|
||||
|
||||
/**
|
||||
* Classes which implement this interface provide methods to describe a class.
|
||||
* Class extending {@link ViewClassInfo} by adding the notion of instantiability.
|
||||
* {@link LayoutParamsParser#getViews()} and {@link LayoutParamsParser#getGroups()} should
|
||||
* only return classes that can be instantiated.
|
||||
*/
|
||||
public interface IClass {
|
||||
|
||||
public String getCanonicalName();
|
||||
|
||||
public IClass getSuperclass();
|
||||
|
||||
public String getSimpleName();
|
||||
|
||||
public IClass getEnclosingClass();
|
||||
|
||||
public IClass[] getDeclaredClasses();
|
||||
|
||||
public boolean isInstantiable();
|
||||
}
|
||||
|
||||
final static class ExtViewClassInfo extends ViewClassInfo {
|
||||
|
||||
private boolean mIsInstantiable;
|
||||
@@ -87,16 +75,16 @@ public class LayoutParamsParser {
|
||||
/* Note: protected members/methods are overridden in unit tests */
|
||||
|
||||
/** Reference to android.view.View */
|
||||
protected IClass mTopViewClass;
|
||||
protected IClassDescriptor mTopViewClass;
|
||||
/** Reference to android.view.ViewGroup */
|
||||
protected IClass mTopGroupClass;
|
||||
protected IClassDescriptor mTopGroupClass;
|
||||
/** Reference to android.view.ViewGroup$LayoutParams */
|
||||
protected IClass mTopLayoutParamsClass;
|
||||
protected IClassDescriptor mTopLayoutParamsClass;
|
||||
|
||||
/** Input list of all classes deriving from android.view.View */
|
||||
protected ArrayList<IClass> mViewList;
|
||||
protected ArrayList<IClassDescriptor> mViewList;
|
||||
/** Input list of all classes deriving from android.view.ViewGroup */
|
||||
protected ArrayList<IClass> mGroupList;
|
||||
protected ArrayList<IClassDescriptor> mGroupList;
|
||||
|
||||
/** Output map of FQCN => info on View classes */
|
||||
protected TreeMap<String, ExtViewClassInfo> mViewMap;
|
||||
@@ -109,14 +97,14 @@ public class LayoutParamsParser {
|
||||
protected AttrsXmlParser mAttrsXmlParser;
|
||||
|
||||
/** The android.jar class loader */
|
||||
protected IAndroidLoader mClassLoader;
|
||||
protected IAndroidClassLoader mClassLoader;
|
||||
|
||||
/**
|
||||
* Instantiate a new LayoutParamsParser.
|
||||
* @param classLoader The android.jar class loader
|
||||
* @param attrsXmlParser The parser of the attrs.xml file
|
||||
*/
|
||||
public LayoutParamsParser(IAndroidLoader classLoader,
|
||||
public LayoutParamsParser(IAndroidClassLoader classLoader,
|
||||
AttrsXmlParser attrsXmlParser) {
|
||||
mClassLoader = classLoader;
|
||||
mAttrsXmlParser = attrsXmlParser;
|
||||
@@ -135,8 +123,8 @@ public class LayoutParamsParser {
|
||||
/**
|
||||
* TODO: doc here.
|
||||
* <p/>
|
||||
* Note: on output we should have NO dependency on IClass, otherwise we wouldn't be able
|
||||
* to unload the class loader later.
|
||||
* Note: on output we should have NO dependency on {@link IClassDescriptor},
|
||||
* otherwise we wouldn't be able to unload the class loader later.
|
||||
* <p/>
|
||||
* Note on Vocabulary: FQCN=Fully Qualified Class Name (e.g. "my.package.class$innerClass")
|
||||
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
|
||||
@@ -168,8 +156,8 @@ public class LayoutParamsParser {
|
||||
if (paramsClassName != null) {
|
||||
superClasses[2] = paramsClassName;
|
||||
}
|
||||
HashMap<String, ArrayList<IClass>> found = mClassLoader.findClassesDerivingFrom(
|
||||
"android.", superClasses);
|
||||
HashMap<String, ArrayList<IClassDescriptor>> found =
|
||||
mClassLoader.findClassesDerivingFrom("android.", superClasses);
|
||||
mTopViewClass = mClassLoader.getClass(rootClassName);
|
||||
mTopGroupClass = mClassLoader.getClass(groupClassName);
|
||||
if (paramsClassName != null) {
|
||||
@@ -196,26 +184,26 @@ public class LayoutParamsParser {
|
||||
|
||||
progress.setWorkRemaining(mGroupList.size() + mViewList.size());
|
||||
|
||||
for (IClass groupChild : mGroupList) {
|
||||
for (IClassDescriptor groupChild : mGroupList) {
|
||||
addGroup(groupChild);
|
||||
progress.worked(1);
|
||||
}
|
||||
|
||||
for (IClass viewChild : mViewList) {
|
||||
for (IClassDescriptor viewChild : mViewList) {
|
||||
if (viewChild != mTopGroupClass) {
|
||||
addView(viewChild);
|
||||
}
|
||||
progress.worked(1);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
CommonPlugin.log(e, "Problem loading class %1$s or %2$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Problem loading class %1$s or %2$s", //$NON-NLS-1$
|
||||
rootClassName, groupClassName);
|
||||
} catch (InvalidAttributeValueException e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
} catch (ClassFormatError e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +211,7 @@ public class LayoutParamsParser {
|
||||
* Parses a View class and adds a ExtViewClassInfo for it in mViewMap.
|
||||
* It calls itself recursively to handle super classes which are also Views.
|
||||
*/
|
||||
private ExtViewClassInfo addView(IClass viewClass) {
|
||||
private ExtViewClassInfo addView(IClassDescriptor viewClass) {
|
||||
String fqcn = viewClass.getCanonicalName();
|
||||
if (mViewMap.containsKey(fqcn)) {
|
||||
return mViewMap.get(fqcn);
|
||||
@@ -238,7 +226,7 @@ public class LayoutParamsParser {
|
||||
// All view classes derive from mTopViewClass by design.
|
||||
// Do not lookup the super class for mTopViewClass itself.
|
||||
if (viewClass.equals(mTopViewClass) == false) {
|
||||
IClass superClass = viewClass.getSuperclass();
|
||||
IClassDescriptor superClass = viewClass.getSuperclass();
|
||||
ExtViewClassInfo superClassInfo = addView(superClass);
|
||||
info.setSuperClass(superClassInfo);
|
||||
}
|
||||
@@ -251,7 +239,7 @@ public class LayoutParamsParser {
|
||||
* Parses a ViewGroup class and adds a ExtViewClassInfo for it in mGroupMap.
|
||||
* It calls itself recursively to handle super classes which are also ViewGroups.
|
||||
*/
|
||||
private ExtViewClassInfo addGroup(IClass groupClass) {
|
||||
private ExtViewClassInfo addGroup(IClassDescriptor groupClass) {
|
||||
String fqcn = groupClass.getCanonicalName();
|
||||
if (mGroupMap.containsKey(fqcn)) {
|
||||
return mGroupMap.get(fqcn);
|
||||
@@ -265,7 +253,7 @@ public class LayoutParamsParser {
|
||||
// android.view.View (i.e. mTopViewClass here). So the only group that can have View as
|
||||
// its super class is the ViewGroup base class and we don't try to resolve it since groups
|
||||
// are loaded before views.
|
||||
IClass superClass = groupClass.getSuperclass();
|
||||
IClassDescriptor superClass = groupClass.getSuperclass();
|
||||
|
||||
// Assertion: at this point, we should have
|
||||
// superClass != mTopViewClass || fqcn.equals(AndroidConstants.CLASS_VIEWGROUP);
|
||||
@@ -291,15 +279,15 @@ public class LayoutParamsParser {
|
||||
*
|
||||
* @return The {@link LayoutParamsInfo} for the ViewGroup class or null.
|
||||
*/
|
||||
private LayoutParamsInfo addLayoutParams(IClass groupClass) {
|
||||
private LayoutParamsInfo addLayoutParams(IClassDescriptor groupClass) {
|
||||
|
||||
// Is there a LayoutParams in this group class?
|
||||
IClass layoutParamsClass = findLayoutParams(groupClass);
|
||||
IClassDescriptor layoutParamsClass = findLayoutParams(groupClass);
|
||||
|
||||
// if there's no layout data in the group class, link to the one from the
|
||||
// super class.
|
||||
if (layoutParamsClass == null) {
|
||||
for (IClass superClass = groupClass.getSuperclass();
|
||||
for (IClassDescriptor superClass = groupClass.getSuperclass();
|
||||
layoutParamsClass == null &&
|
||||
superClass != null &&
|
||||
superClass.equals(mTopViewClass) == false;
|
||||
@@ -319,7 +307,7 @@ public class LayoutParamsParser {
|
||||
* Parses a LayoutParams class and returns a LayoutParamsInfo object for it.
|
||||
* It calls itself recursively to handle the super class of the LayoutParams.
|
||||
*/
|
||||
private LayoutParamsInfo getLayoutParamsInfo(IClass layoutParamsClass) {
|
||||
private LayoutParamsInfo getLayoutParamsInfo(IClassDescriptor layoutParamsClass) {
|
||||
String fqcn = layoutParamsClass.getCanonicalName();
|
||||
LayoutParamsInfo layoutParamsInfo = mLayoutParamsMap.get(fqcn);
|
||||
|
||||
@@ -330,7 +318,7 @@ public class LayoutParamsParser {
|
||||
// Find the link on the LayoutParams super class
|
||||
LayoutParamsInfo superClassInfo = null;
|
||||
if (layoutParamsClass.equals(mTopLayoutParamsClass) == false) {
|
||||
IClass superClass = layoutParamsClass.getSuperclass();
|
||||
IClassDescriptor superClass = layoutParamsClass.getSuperclass();
|
||||
superClassInfo = getLayoutParamsInfo(superClass);
|
||||
}
|
||||
|
||||
@@ -355,9 +343,9 @@ public class LayoutParamsParser {
|
||||
* @param groupClass The ViewGroup derived class
|
||||
* @return The Class of the inner LayoutParams or null if none is declared.
|
||||
*/
|
||||
private IClass findLayoutParams(IClass groupClass) {
|
||||
IClass[] innerClasses = groupClass.getDeclaredClasses();
|
||||
for (IClass innerClass : innerClasses) {
|
||||
private IClassDescriptor findLayoutParams(IClassDescriptor groupClass) {
|
||||
IClassDescriptor[] innerClasses = groupClass.getDeclaredClasses();
|
||||
for (IClassDescriptor innerClass : innerClasses) {
|
||||
if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_LAYOUTPARAMS)) {
|
||||
return innerClass;
|
||||
}
|
||||
@@ -365,12 +353,16 @@ public class LayoutParamsParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and return a list of ViewClassInfo from a map by filtering out the class that
|
||||
* cannot be instantiated.
|
||||
*/
|
||||
private List<ViewClassInfo> getInstantiables(SortedMap<String, ExtViewClassInfo> map) {
|
||||
Collection<ExtViewClassInfo> values = map.values();
|
||||
ArrayList<ViewClassInfo> list = new ArrayList<ViewClassInfo>();
|
||||
|
||||
for (ExtViewClassInfo info : values) {
|
||||
if (info.mIsInstantiable) {
|
||||
if (info.isInstantiable()) {
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.sdk;
|
||||
|
||||
/**
|
||||
* Enum for loading status of various SDK parts.
|
||||
*/
|
||||
public enum LoadStatus {
|
||||
LOADING, LOADED, FAILED;
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.sdk;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.ISdkLog;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.android.sdklib.project.ProjectProperties;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Central point to load, manipulate and deal with the Android SDK. Only one SDK can be used
|
||||
* at the same time.
|
||||
*
|
||||
* To start using an SDK, call {@link #loadSdk(String)} which returns the instance of
|
||||
* the Sdk object.
|
||||
*
|
||||
* To get the list of platforms present in the SDK, call {@link #getPlatforms()}.
|
||||
* To get the list of add-ons present in the SDK, call {@link #getAddons()}.
|
||||
*
|
||||
*/
|
||||
public class Sdk {
|
||||
private final static String PROPERTY_PROJECT_TARGET = "androidTarget"; //$NON-NLS-1$
|
||||
|
||||
private static Sdk sCurrentSdk = null;
|
||||
|
||||
private final SdkManager mManager;
|
||||
private final HashMap<IProject, IAndroidTarget> mProjectMap =
|
||||
new HashMap<IProject, IAndroidTarget>();
|
||||
private final HashMap<IAndroidTarget, AndroidTargetData> mTargetMap =
|
||||
new HashMap<IAndroidTarget, AndroidTargetData>();
|
||||
private final String mDocBaseUrl;
|
||||
|
||||
/**
|
||||
* Loads an SDK and returns an {@link Sdk} object if success.
|
||||
* @param sdkLocation the OS path to the SDK.
|
||||
*/
|
||||
public static Sdk loadSdk(String sdkLocation) {
|
||||
if (sCurrentSdk != null) {
|
||||
// manual unload?
|
||||
sCurrentSdk = null;
|
||||
}
|
||||
|
||||
final ArrayList<String> logMessages = new ArrayList<String>();
|
||||
ISdkLog log = new ISdkLog() {
|
||||
public void error(String errorFormat, Object... arg) {
|
||||
logMessages.add(String.format(errorFormat, arg));
|
||||
}
|
||||
public void warning(String warningFormat, Object... arg) {
|
||||
logMessages.add(String.format(warningFormat, arg));
|
||||
}
|
||||
};
|
||||
|
||||
// get an SdkManager object for the location
|
||||
SdkManager manager = SdkManager.createManager(sdkLocation, log);
|
||||
if (manager != null) {
|
||||
sCurrentSdk = new Sdk(manager);
|
||||
return sCurrentSdk;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder("Error Loading the SDK:\n");
|
||||
for (String msg : logMessages) {
|
||||
sb.append('\n');
|
||||
sb.append(msg);
|
||||
}
|
||||
AdtPlugin.displayError("Android SDK", sb.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link Sdk} object.
|
||||
*/
|
||||
public static Sdk getCurrent() {
|
||||
return sCurrentSdk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the local documentation.
|
||||
* Can return null if no documentation is found in the current SDK.
|
||||
*
|
||||
* @return A file:// URL on the local documentation folder if it exists or null.
|
||||
*/
|
||||
public String getDocumentationBaseUrl() {
|
||||
return mDocBaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of targets that are available in the SDK.
|
||||
*/
|
||||
public IAndroidTarget[] getTargets() {
|
||||
return mManager.getTargets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}.
|
||||
* @param hash the hash
|
||||
*/
|
||||
public IAndroidTarget getTargetFromHashString(String hash) {
|
||||
return mManager.getTargetFromHashString(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates an {@link IProject} and an {@link IAndroidTarget}.
|
||||
*/
|
||||
public void setProject(IProject project, IAndroidTarget target) {
|
||||
synchronized (mProjectMap) {
|
||||
// look for the current target of the project
|
||||
IAndroidTarget previousTarget = mProjectMap.get(project);
|
||||
|
||||
if (target != previousTarget) {
|
||||
// save the target hash string in the project persistent property
|
||||
setProjectTargetHashString(project, target.hashString());
|
||||
|
||||
// put it in a local map for easy access.
|
||||
mProjectMap.put(project, target);
|
||||
|
||||
// recompile the project if needed.
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
AndroidClasspathContainerInitializer.updateProjects(
|
||||
new IJavaProject[] { javaProject });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IAndroidTarget} object associated with the given {@link IProject}.
|
||||
*/
|
||||
public IAndroidTarget getTarget(IProject project) {
|
||||
synchronized (mProjectMap) {
|
||||
IAndroidTarget target = mProjectMap.get(project);
|
||||
if (target == null) {
|
||||
// get the value from the project persistent property.
|
||||
String targetHashString = getProjectTargetHashString(project);
|
||||
|
||||
if (targetHashString != null) {
|
||||
target = mManager.getTargetFromHashString(targetHashString);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash string uniquely identifying the target of a project. This methods reads
|
||||
* the string from the project persistent preferences/properties.
|
||||
* <p/>The string is equivalent to the return of {@link IAndroidTarget#hashString()}.
|
||||
* @param project The project for which to return the target hash string.
|
||||
* @return the hash string or null if the project does not have a target set.
|
||||
*/
|
||||
public static String getProjectTargetHashString(IProject project) {
|
||||
// load the default.properties from the project folder.
|
||||
ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString());
|
||||
if (properties == null) {
|
||||
AdtPlugin.log(IStatus.ERROR, "Failed to load properties file for project '%s'",
|
||||
project.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
return properties.getProperty(ProjectProperties.PROPERTY_TARGET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a target hash string in a project's persistent preferences/property storage.
|
||||
* @param project The project in which to save the hash string.
|
||||
* @param targetHashString The target hash string to save. This must be the result from
|
||||
* {@link IAndroidTarget#hashString()}.
|
||||
*/
|
||||
public static void setProjectTargetHashString(IProject project, String targetHashString) {
|
||||
// because we don't want to erase other properties from default.properties, we first load
|
||||
// them
|
||||
ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString());
|
||||
if (properties == null) {
|
||||
// doesn't exist yet? we create it.
|
||||
properties = ProjectProperties.create(project.getLocation().toOSString());
|
||||
}
|
||||
|
||||
// add/change the target hash string.
|
||||
properties.setProperty(ProjectProperties.PROPERTY_TARGET, targetHashString);
|
||||
|
||||
// and rewrite the file.
|
||||
try {
|
||||
properties.save();
|
||||
} catch (IOException e) {
|
||||
AdtPlugin.log(e, "Failed to save default.properties for project '%s'",
|
||||
project.getName());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return the {@link PlatformData} for a given {@link IAndroidTarget}.
|
||||
*/
|
||||
public AndroidTargetData getTargetData(IAndroidTarget target) {
|
||||
synchronized (mTargetMap) {
|
||||
return mTargetMap.get(target);
|
||||
}
|
||||
}
|
||||
|
||||
private Sdk(SdkManager manager) {
|
||||
mManager = manager;
|
||||
|
||||
// pre-compute some paths
|
||||
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +
|
||||
SdkConstants.OS_SDK_DOCS_FOLDER);
|
||||
}
|
||||
|
||||
void setTargetData(IAndroidTarget target, AndroidTargetData data) {
|
||||
synchronized (mTargetMap) {
|
||||
mTargetMap.put(target, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the local documentation.
|
||||
* Can return null if no documentation is found in the current SDK.
|
||||
*
|
||||
* @param osDocsPath Path to the documentation folder in the current SDK.
|
||||
* The folder may not actually exist.
|
||||
* @return A file:// URL on the local documentation folder if it exists or null.
|
||||
*/
|
||||
private String getDocumentationBaseUrl(String osDocsPath) {
|
||||
File f = new File(osDocsPath);
|
||||
|
||||
if (f.isDirectory()) {
|
||||
try {
|
||||
// Note: to create a file:// URL, one would typically use something like
|
||||
// f.toURI().toURL().toString(). However this generates a broken path on
|
||||
// Windows, namely "C:\\foo" is converted to "file:/C:/foo" instead of
|
||||
// "file:///C:/foo" (i.e. there should be 3 / after "file:"). So we'll
|
||||
// do the correct thing manually.
|
||||
|
||||
String path = f.getAbsolutePath();
|
||||
if (File.separatorChar != '/') {
|
||||
path = path.replace(File.separatorChar, '/');
|
||||
}
|
||||
|
||||
// For some reason the URL class doesn't add the mandatory "//" after
|
||||
// the "file:" protocol name, so it has to be hacked into the path.
|
||||
URL url = new URL("file", null, "//" + path); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
String result = url.toString();
|
||||
return result;
|
||||
} catch (MalformedURLException e) {
|
||||
// ignore malformed URLs
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.resources;
|
||||
package com.android.ide.eclipse.adt.sdk;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
@@ -42,18 +41,19 @@ import javax.management.InvalidAttributeValueException;
|
||||
* where code is a single letter (W for widget, L for layout, P for layout params), and class names
|
||||
* are the fully qualified name of the classes.
|
||||
*/
|
||||
public final class WidgetListLoader implements IAndroidLoader {
|
||||
public final class WidgetClassLoader implements IAndroidClassLoader {
|
||||
|
||||
/**
|
||||
* Basic class containing the class descriptions found in the text file.
|
||||
*/
|
||||
private final static class ClassDescriptor implements IClass {
|
||||
private final static class ClassDescriptor implements IClassDescriptor {
|
||||
|
||||
private String mName;
|
||||
private String mSimpleName;
|
||||
private ClassDescriptor mSuperClass;
|
||||
private ClassDescriptor mEnclosingClass;
|
||||
private final ArrayList<IClass> mDeclaredClasses = new ArrayList<IClass>();
|
||||
private final ArrayList<IClassDescriptor> mDeclaredClasses =
|
||||
new ArrayList<IClassDescriptor>();
|
||||
private boolean mIsInstantiable = false;
|
||||
|
||||
ClassDescriptor(String fqcn) {
|
||||
@@ -69,15 +69,15 @@ public final class WidgetListLoader implements IAndroidLoader {
|
||||
return mSimpleName;
|
||||
}
|
||||
|
||||
public IClass[] getDeclaredClasses() {
|
||||
return mDeclaredClasses.toArray(new IClass[mDeclaredClasses.size()]);
|
||||
public IClassDescriptor[] getDeclaredClasses() {
|
||||
return mDeclaredClasses.toArray(new IClassDescriptor[mDeclaredClasses.size()]);
|
||||
}
|
||||
|
||||
private void addDeclaredClass(ClassDescriptor declaredClass) {
|
||||
mDeclaredClasses.add(declaredClass);
|
||||
}
|
||||
|
||||
public IClass getEnclosingClass() {
|
||||
public IClassDescriptor getEnclosingClass() {
|
||||
return mEnclosingClass;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public final class WidgetListLoader implements IAndroidLoader {
|
||||
mName = enclosingClass.mName + "$" + mName.substring(enclosingClass.mName.length() + 1);
|
||||
}
|
||||
|
||||
public IClass getSuperclass() {
|
||||
public IClassDescriptor getSuperclass() {
|
||||
return mSuperClass;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public final class WidgetListLoader implements IAndroidLoader {
|
||||
* @param osFilePath the OS path of the file to load.
|
||||
* @throws FileNotFoundException if the file is not found.
|
||||
*/
|
||||
WidgetListLoader(String osFilePath) throws FileNotFoundException {
|
||||
WidgetClassLoader(String osFilePath) throws FileNotFoundException {
|
||||
mOsFilePath = osFilePath;
|
||||
mReader = new BufferedReader(new FileReader(osFilePath));
|
||||
}
|
||||
@@ -301,20 +301,21 @@ public final class WidgetListLoader implements IAndroidLoader {
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(String rootPackage,
|
||||
public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(String rootPackage,
|
||||
String[] superClasses) throws IOException, InvalidAttributeValueException,
|
||||
ClassFormatError {
|
||||
HashMap<String, ArrayList<IClass>> map = new HashMap<String, ArrayList<IClass>>();
|
||||
HashMap<String, ArrayList<IClassDescriptor>> map =
|
||||
new HashMap<String, ArrayList<IClassDescriptor>>();
|
||||
|
||||
ArrayList<IClass> list = new ArrayList<IClass>();
|
||||
ArrayList<IClassDescriptor> list = new ArrayList<IClassDescriptor>();
|
||||
list.addAll(mWidgetMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEW, list);
|
||||
|
||||
list = new ArrayList<IClass>();
|
||||
list = new ArrayList<IClassDescriptor>();
|
||||
list.addAll(mLayoutMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEWGROUP, list);
|
||||
|
||||
list = new ArrayList<IClass>();
|
||||
list = new ArrayList<IClassDescriptor>();
|
||||
list.addAll(mLayoutParamsMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list);
|
||||
|
||||
@@ -326,7 +327,7 @@ public final class WidgetListLoader implements IAndroidLoader {
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException {
|
||||
public IClassDescriptor getClass(String className) throws ClassNotFoundException {
|
||||
return mMap.get(className);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
* org.eclipse.jdt.internal.ui.wizards.JavaProjectWizardFirstPage
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.project.internal;
|
||||
package com.android.ide.eclipse.adt.wizards.newproject;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdkuilib.SdkTargetSelector;
|
||||
|
||||
import org.eclipse.core.filesystem.URIUtil;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
@@ -120,6 +122,7 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
private boolean mInternalActivityNameUpdate;
|
||||
protected boolean mProjectNameModifiedByUser;
|
||||
protected boolean mApplicationNameModifiedByUser;
|
||||
private SdkTargetSelector mSdkTargetSelector;
|
||||
|
||||
|
||||
/**
|
||||
@@ -133,7 +136,9 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
if (sCustomLocationOsPath == null ||
|
||||
sCustomLocationOsPath.length() == 0 ||
|
||||
!new File(sCustomLocationOsPath).isDirectory()) {
|
||||
sCustomLocationOsPath = AdtPlugin.getOsSdkSamplesFolder();
|
||||
// FIXME location of samples is pretty much impossible here.
|
||||
//sCustomLocationOsPath = AdtPlugin.getOsSdkSamplesFolder();
|
||||
sCustomLocationOsPath = File.listRoots()[0].getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +205,11 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
return mSourceFolder;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the current sdk target or null if none has been selected yet. */
|
||||
public IAndroidTarget getSdkTarget() {
|
||||
return mSdkTargetSelector == null ? null : mSdkTargetSelector.getFirstSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides @DialogPage.setVisible(boolean) to put the focus in the project name when
|
||||
@@ -232,15 +242,19 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
|
||||
createProjectNameGroup(composite);
|
||||
createLocationGroup(composite);
|
||||
createTargetGroup(composite);
|
||||
createPropertiesGroup(composite);
|
||||
|
||||
// Update state the first time
|
||||
enableLocationWidgets();
|
||||
setPageComplete(validatePage());
|
||||
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setControl(composite);
|
||||
|
||||
// Validate. This will complain about the first empty field.
|
||||
setPageComplete(validatePage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,6 +370,33 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the target group.
|
||||
* It only contains an SdkTargetSelector.
|
||||
*/
|
||||
private void createTargetGroup(Composite parent) {
|
||||
Group group = new Group(parent, SWT.SHADOW_ETCHED_IN);
|
||||
// Layout has 1 column
|
||||
group.setLayout(new GridLayout());
|
||||
group.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
group.setFont(parent.getFont());
|
||||
group.setText("Target");
|
||||
|
||||
// get the targets from the sdk
|
||||
IAndroidTarget[] targets = null;
|
||||
if (Sdk.getCurrent() != null) {
|
||||
targets = Sdk.getCurrent().getTargets();
|
||||
}
|
||||
|
||||
mSdkTargetSelector = new SdkTargetSelector(group, targets, false /*multi-selection*/);
|
||||
mSdkTargetSelector.setSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
setPageComplete(validatePage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a directory browser and update the location path field with the selected path
|
||||
*/
|
||||
@@ -754,6 +795,9 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
if ((status & MSG_ERROR) == 0) {
|
||||
status |= validateLocationPath(workspace);
|
||||
}
|
||||
if ((status & MSG_ERROR) == 0) {
|
||||
status |= validateSdkTarget();
|
||||
}
|
||||
if ((status & MSG_ERROR) == 0) {
|
||||
status |= validatePackageField();
|
||||
}
|
||||
@@ -893,6 +937,18 @@ public class NewProjectCreationPage extends WizardPage {
|
||||
return MSG_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the sdk target choice.
|
||||
*
|
||||
* @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.
|
||||
*/
|
||||
private int validateSdkTarget() {
|
||||
if (getSdkTarget() == null) {
|
||||
return setStatus("An SDK Target must be specified.", MSG_ERROR);
|
||||
}
|
||||
return MSG_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the activity name field.
|
||||
*
|
||||
@@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.project.internal;
|
||||
package com.android.ide.eclipse.adt.wizards.newproject;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.AndroidNature;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
@@ -60,6 +62,7 @@ import java.net.MalformedURLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* A "New Android Project" Wizard.
|
||||
@@ -81,6 +84,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
private static final String PARAM_STRING_CONTENT = "STRING_CONTENT"; //$NON-NLS-1$
|
||||
private static final String PARAM_IS_NEW_PROJECT = "IS_NEW_PROJECT"; //$NON-NLS-1$
|
||||
private static final String PARAM_SRC_FOLDER = "SRC_FOLDER"; //$NON-NLS-1$
|
||||
private static final String PARAM_SDK_TARGET = "SDK_TARGET"; //$NON-NLS-1$
|
||||
|
||||
private static final String PH_ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$
|
||||
private static final String PH_INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$
|
||||
@@ -223,13 +227,14 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
final IProject project = workspace.getRoot().getProject(mMainPage.getProjectName());
|
||||
final IProjectDescription description = workspace.newProjectDescription(project.getName());
|
||||
|
||||
final Map<String, String> parameters = new HashMap<String, String>();
|
||||
final Map<String, Object> parameters = new HashMap<String, Object>();
|
||||
parameters.put(PARAM_PROJECT, mMainPage.getProjectName());
|
||||
parameters.put(PARAM_PACKAGE, mMainPage.getPackageName());
|
||||
parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME);
|
||||
parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder());
|
||||
parameters.put(PARAM_IS_NEW_PROJECT, Boolean.toString(mMainPage.isNewProject()));
|
||||
parameters.put(PARAM_IS_NEW_PROJECT, mMainPage.isNewProject());
|
||||
parameters.put(PARAM_SRC_FOLDER, mMainPage.getSourceFolder());
|
||||
parameters.put(PARAM_SDK_TARGET, mMainPage.getSdkTarget());
|
||||
|
||||
if (mMainPage.isCreateActivity()) {
|
||||
// An activity name can be of the form ".package.Class" or ".Class".
|
||||
@@ -315,7 +320,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
* to create or modify the project or if it is canceled by the user.
|
||||
*/
|
||||
private void createProjectAsync(IProject project, IProjectDescription description,
|
||||
IProgressMonitor monitor, Map<String, String> parameters,
|
||||
IProgressMonitor monitor, Map<String, Object> parameters,
|
||||
Map<String, String> stringDictionary)
|
||||
throws InvocationTargetException {
|
||||
monitor.beginTask("Create Android Project", 100);
|
||||
@@ -330,7 +335,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
|
||||
// Create folders in the project if they don't already exist
|
||||
addDefaultDirectories(project, AndroidConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor);
|
||||
String[] sourceFolder = new String[] { parameters.get(PARAM_SRC_FOLDER) };
|
||||
String[] sourceFolder = new String[] { (String) parameters.get(PARAM_SRC_FOLDER) };
|
||||
addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolder, monitor);
|
||||
|
||||
// Create the resource folders in the project if they don't already exist.
|
||||
@@ -340,7 +345,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
setupSourceFolder(javaProject, sourceFolder[0], monitor);
|
||||
|
||||
if (Boolean.parseBoolean(parameters.get(PARAM_IS_NEW_PROJECT))) {
|
||||
if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) {
|
||||
// Create files in the project if they don't already exist
|
||||
addManifest(project, parameters, stringDictionary, monitor);
|
||||
|
||||
@@ -360,6 +365,8 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
monitor);
|
||||
}
|
||||
|
||||
Sdk.getCurrent().setProject(project, (IAndroidTarget) parameters.get(PARAM_SDK_TARGET));
|
||||
|
||||
// Fix the project to make sure all properties are as expected.
|
||||
// Necessary for existing projects and good for new ones to.
|
||||
ProjectHelper.fixProject(project);
|
||||
@@ -409,7 +416,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
* @throws IOException if the method fails to create the files in the
|
||||
* project.
|
||||
*/
|
||||
private void addManifest(IProject project, Map<String, String> parameters,
|
||||
private void addManifest(IProject project, Map<String, Object> parameters,
|
||||
Map<String, String> stringDictionary, IProgressMonitor monitor)
|
||||
throws CoreException, IOException {
|
||||
|
||||
@@ -543,16 +550,16 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
* project.
|
||||
*/
|
||||
private void addSampleCode(IProject project, String sourceFolder,
|
||||
Map<String, String> parameters, Map<String, String> stringDictionary,
|
||||
Map<String, Object> parameters, Map<String, String> stringDictionary,
|
||||
IProgressMonitor monitor) throws CoreException, IOException {
|
||||
// create the java package directories.
|
||||
IFolder pkgFolder = project.getFolder(sourceFolder);
|
||||
String packageName = parameters.get(PARAM_PACKAGE);
|
||||
String packageName = (String) parameters.get(PARAM_PACKAGE);
|
||||
|
||||
// The PARAM_ACTIVITY key will be absent if no activity should be created,
|
||||
// in which case activityName will be null.
|
||||
String activityName = parameters.get(PARAM_ACTIVITY);
|
||||
Map<String, String> java_activity_parameters = parameters;
|
||||
String activityName = (String) parameters.get(PARAM_ACTIVITY);
|
||||
Map<String, Object> java_activity_parameters = parameters;
|
||||
if (activityName != null) {
|
||||
if (activityName.indexOf('.') >= 0) {
|
||||
// There are package names in the activity name. Transform packageName to add
|
||||
@@ -564,7 +571,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
|
||||
// Also update the values used in the JAVA_FILE_TEMPLATE below
|
||||
// (but not the ones from the manifest so don't change the caller's dictionary)
|
||||
java_activity_parameters = new HashMap<String, String>(parameters);
|
||||
java_activity_parameters = new HashMap<String, Object>(parameters);
|
||||
java_activity_parameters.put(PARAM_PACKAGE, packageName);
|
||||
java_activity_parameters.put(PARAM_ACTIVITY, activityName);
|
||||
}
|
||||
@@ -665,7 +672,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
* length.
|
||||
*/
|
||||
private void copyFile(String resourceFilename, IFile destFile,
|
||||
Map<String, String> parameters, IProgressMonitor monitor)
|
||||
Map<String, Object> parameters, IProgressMonitor monitor)
|
||||
throws CoreException, IOException {
|
||||
|
||||
// Read existing file.
|
||||
@@ -692,13 +699,14 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
* Replaces placeholders found in a string with values.
|
||||
*
|
||||
* @param str the string to search for placeholders.
|
||||
* @param parameters a map of <placeholder, Value> to search for in the
|
||||
* string
|
||||
* @param parameters a map of <placeholder, Value> to search for in the string
|
||||
* @return A new String object with the placeholder replaced by the values.
|
||||
*/
|
||||
private String replaceParameters(String str, Map<String, String> parameters) {
|
||||
for (String key : parameters.keySet()) {
|
||||
str = str.replaceAll(key, parameters.get(key));
|
||||
private String replaceParameters(String str, Map<String, Object> parameters) {
|
||||
for (Entry<String, Object> entry : parameters.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
str = str.replaceAll(entry.getKey(), (String) entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.ide.eclipse.common;
|
||||
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -38,8 +40,11 @@ import java.io.File;
|
||||
*
|
||||
*/
|
||||
public class AndroidConstants {
|
||||
/** The Editors Plugin ID */
|
||||
public static final String EDITORS_PLUGIN_ID = "com.android.ide.eclipse.editors"; // $NON-NLS-1$
|
||||
/**
|
||||
* The old Editors Plugin ID. It is still used in some places for compatibility.
|
||||
* Please do not use for new features.
|
||||
*/
|
||||
public static final String EDITORS_NAMESPACE = "com.android.ide.eclipse.editors"; // $NON-NLS-1$
|
||||
|
||||
/** Nature of android projects */
|
||||
public final static String NATURE = "com.android.ide.eclipse.adt.AndroidNature"; //$NON-NLS-1$
|
||||
@@ -72,6 +77,8 @@ public class AndroidConstants {
|
||||
public final static String EXT_JAR = "jar"; //$NON-NLS-1$
|
||||
/** Extension of aidl files, i.e. "aidl" */
|
||||
public final static String EXT_AIDL = "aidl"; //$NON-NLS-1$
|
||||
/** Extension of native libraries, i.e. "so" */
|
||||
public final static String EXT_NATIVE_LIB = "so"; //$NON-NLS-1$
|
||||
|
||||
private final static String DOT = "."; //$NON-NLS-1$
|
||||
|
||||
@@ -90,17 +97,8 @@ public class AndroidConstants {
|
||||
|
||||
/** Name of the manifest file, i.e. "AndroidManifest.xml". */
|
||||
public static final String FN_ANDROID_MANIFEST = "AndroidManifest.xml"; //$NON-NLS-1$
|
||||
public static final String FN_PROJECT_AIDL = "project.aidl"; //$NON-NLS-1$
|
||||
|
||||
/** Name of the framework library, i.e. "android.jar" */
|
||||
public static final String FN_FRAMEWORK_LIBRARY = "android.jar"; //$NON-NLS-1$
|
||||
/** Name of the layout attributes, i.e. "attrs.xml" */
|
||||
public static final String FN_ATTRS_XML = "attrs.xml"; //$NON-NLS-1$
|
||||
/** Name of the layout attributes, i.e. "attrs_manifest.xml" */
|
||||
public static final String FN_ATTRS_MANIFEST_XML = "attrs_manifest.xml"; //$NON-NLS-1$
|
||||
/** framework aidl import file */
|
||||
public static final String FN_FRAMEWORK_AIDL = "framework.aidl"; //$NON-NLS-1$
|
||||
/** layoutlib.jar file */
|
||||
public static final String FN_LAYOUTLIB_JAR = "layoutlib.jar"; //$NON-NLS-1$
|
||||
/** dex.jar file */
|
||||
public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$
|
||||
/** Name of the android sources directory */
|
||||
@@ -116,10 +114,6 @@ public class AndroidConstants {
|
||||
public final static String FN_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$
|
||||
/** Temporary packaged resources file name, i.e. "resources.ap_" */
|
||||
public final static String FN_RESOURCES_AP_ = "resources.ap_"; //$NON-NLS-1$
|
||||
/** build properties file */
|
||||
public final static String FN_BUILD_PROP = "build.prop"; //$NON-NLS-1$
|
||||
/** plugin properties file */
|
||||
public final static String FN_PLUGIN_PROP = "plugin.prop"; //$NON-NLS-1$
|
||||
|
||||
public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
@@ -136,15 +130,21 @@ public class AndroidConstants {
|
||||
public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** Skin layout file */
|
||||
public final static String FN_LAYOUT = "layout";//$NON-NLS-1$
|
||||
/** Folder Names for Android Projects . */
|
||||
|
||||
/** Resources folder name, i.e. "res". */
|
||||
/* Resources folder name, i.e. "res". */
|
||||
public final static String FD_RESOURCES = "res"; //$NON-NLS-1$
|
||||
/** Assets folder name, i.e. "assets" */
|
||||
public final static String FD_ASSETS = "assets"; //$NON-NLS-1$
|
||||
/** Default source folder name, i.e. "src" */
|
||||
public final static String FD_SOURCES = "src"; //$NON-NLS-1$
|
||||
/** Default native library folder name inside the project, i.e. "libs"
|
||||
* While the folder inside the .apk is "lib", we call that one libs because
|
||||
* that's what we use in ant for both .jar and .so and we need to make the 2 development ways
|
||||
* compatible. */
|
||||
public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$
|
||||
/** Native lib folder inside the APK: "lib" */
|
||||
public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$
|
||||
/** Default bin folder name, i.e. "bin" */
|
||||
public final static String FD_BINARIES = "bin"; //$NON-NLS-1$
|
||||
/** Default anim resource folder name, i.e. "anim" */
|
||||
@@ -163,22 +163,6 @@ public class AndroidConstants {
|
||||
public final static String FD_XML = "xml"; //$NON-NLS-1$
|
||||
/** Default raw resource folder name, i.e. "raw" */
|
||||
public final static String FD_RAW = "raw"; //$NON-NLS-1$
|
||||
/** Name of the tools folder. */
|
||||
public final static String FD_TOOLS = "tools"; //$NON-NLS-1$
|
||||
/** Name of the libs folder. */
|
||||
public final static String FD_LIBS = "lib"; //$NON-NLS-1$
|
||||
/** Name of the docs folder. */
|
||||
public final static String FD_DOCS = "docs"; //$NON-NLS-1$
|
||||
/** Name of the images folder. */
|
||||
public final static String FD_IMAGES = "images"; //$NON-NLS-1$
|
||||
/** Name of the skins folder. */
|
||||
public final static String FD_SKINS = "skins"; //$NON-NLS-1$
|
||||
/** Name of the samples folder. */
|
||||
public final static String FD_SAMPLES = "samples"; //$NON-NLS-1$
|
||||
/** Name of the folder containing the default framework resources. */
|
||||
public final static String FD_DEFAULT_RES = "default"; //$NON-NLS-1$
|
||||
/** SDK font folder name, i.e. "fonts" */
|
||||
public final static String FD_FONTS = "fonts"; //$NON-NLS-1$
|
||||
|
||||
/** Absolute path of the workspace root, i.e. "/" */
|
||||
public final static String WS_ROOT = WS_SEP;
|
||||
@@ -190,57 +174,16 @@ public class AndroidConstants {
|
||||
public final static String WS_ASSETS = WS_SEP + FD_ASSETS;
|
||||
|
||||
/** Leaf of the javaDoc folder. Does not start with a separator. */
|
||||
public final static String WS_JAVADOC_FOLDER_LEAF = FD_DOCS + "/reference"; //$NON-NLS-1$
|
||||
|
||||
/** Path of the documentation directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator;
|
||||
|
||||
/** Path of the tools directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator;
|
||||
public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/reference"; //$NON-NLS-1$
|
||||
|
||||
/** Path of the samples directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_SAMPLES_FOLDER = FD_SAMPLES + File.separator;
|
||||
|
||||
/** Path of the lib directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_LIBS_FOLDER =
|
||||
OS_SDK_TOOLS_FOLDER + FD_LIBS + File.separator;
|
||||
|
||||
/** Path of the resources directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_RESOURCES_FOLDER =
|
||||
OS_SDK_LIBS_FOLDER + FD_RESOURCES + File.separator;
|
||||
|
||||
/** Path of the resources directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_FONTS_FOLDER =
|
||||
OS_SDK_LIBS_FOLDER + FD_FONTS + File.separator;
|
||||
|
||||
/** Path of the skin directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator. */
|
||||
public final static String OS_SDK_SKINS_FOLDER =
|
||||
OS_SDK_LIBS_FOLDER + FD_IMAGES + File.separator + FD_SKINS + File.separator;
|
||||
|
||||
/** Path of the attrs.xml file relative to the sdk folder. */
|
||||
public final static String OS_SDK_ATTRS_XML =
|
||||
OS_SDK_RESOURCES_FOLDER + File.separator + FD_DEFAULT_RES + File.separator +
|
||||
FD_VALUES + File.separator + FN_ATTRS_XML;
|
||||
|
||||
/** Path of the attrs_manifest.xml file relative to the sdk folder. */
|
||||
public final static String OS_SDK_ATTRS_MANIFEST_XML =
|
||||
OS_SDK_RESOURCES_FOLDER + File.separator + FD_DEFAULT_RES + File.separator +
|
||||
FD_VALUES + File.separator + FN_ATTRS_MANIFEST_XML;
|
||||
|
||||
/** Path of the layoutlib.jar file relative to the sdk folder. */
|
||||
public final static String OS_SDK_LIBS_LAYOUTLIB_JAR =
|
||||
OS_SDK_LIBS_FOLDER + FN_LAYOUTLIB_JAR;
|
||||
* This is an OS path, ending with a separator.
|
||||
* FIXME: remove once the NPW is fixed. */
|
||||
public final static String OS_SDK_SAMPLES_FOLDER = SdkConstants.FD_SAMPLES + File.separator;
|
||||
|
||||
/** Path of the dx.jar file relative to the sdk folder. */
|
||||
public final static String OS_SDK_LIBS_DX_JAR =
|
||||
OS_SDK_LIBS_FOLDER + FN_DX_JAR;
|
||||
SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + FN_DX_JAR;
|
||||
|
||||
/** Regexp for single dot */
|
||||
public final static String RE_DOT = "\\."; //$NON-NLS-1$
|
||||
@@ -255,17 +198,20 @@ public class AndroidConstants {
|
||||
/** Namespace pattern for the custom resource XML, i.e. "http://schemas.android.com/apk/res/%s" */
|
||||
public final static String NS_CUSTOM_RESOURCES = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$
|
||||
|
||||
/** The old common plug-in ID. Please do not use for new features. */
|
||||
public static final String COMMON_PLUGIN_ID = "com.android.ide.eclipse.common"; //$NON-NLS-1$
|
||||
|
||||
/** aapt marker error. */
|
||||
public final static String MARKER_AAPT = CommonPlugin.PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$
|
||||
public final static String MARKER_AAPT = COMMON_PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$
|
||||
|
||||
/** XML marker error. */
|
||||
public final static String MARKER_XML = CommonPlugin.PLUGIN_ID + ".xmlProblem"; //$NON-NLS-1$
|
||||
public final static String MARKER_XML = COMMON_PLUGIN_ID + ".xmlProblem"; //$NON-NLS-1$
|
||||
|
||||
/** aidl marker error. */
|
||||
public final static String MARKER_AIDL = CommonPlugin.PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$
|
||||
public final static String MARKER_AIDL = COMMON_PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$
|
||||
|
||||
/** android marker error */
|
||||
public final static String MARKER_ANDROID = CommonPlugin.PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$
|
||||
public final static String MARKER_ANDROID = COMMON_PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$
|
||||
|
||||
/** Name for the "type" marker attribute */
|
||||
public final static String MARKER_ATTR_TYPE = "android.type"; //$NON-NLS-1$
|
||||
@@ -301,6 +247,7 @@ public class AndroidConstants {
|
||||
public final static String CLASS_PREFERENCES =
|
||||
"android.preference." + CLASS_PREFERENCE_SCREEN; //$NON-NLS-1$
|
||||
public final static String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$
|
||||
public final static String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$
|
||||
|
||||
public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class EclipseUiHelper {
|
||||
try {
|
||||
IViewPart part = page.showView(viewId,
|
||||
null /* secondaryId */,
|
||||
activate ? page.VIEW_ACTIVATE : page.VIEW_VISIBLE);
|
||||
activate ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_VISIBLE);
|
||||
} catch (PartInitException e) {
|
||||
// ignore
|
||||
}
|
||||
@@ -44,6 +44,7 @@ public class AndroidManifestParser {
|
||||
private final static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
|
||||
private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$
|
||||
private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$
|
||||
private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$
|
||||
private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$
|
||||
private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$
|
||||
private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$
|
||||
@@ -53,6 +54,7 @@ public class AndroidManifestParser {
|
||||
private final static String NODE_INTENT = "intent-filter"; //$NON-NLS-1$
|
||||
private final static String NODE_ACTION = "action"; //$NON-NLS-1$
|
||||
private final static String NODE_CATEGORY = "category"; //$NON-NLS-1$
|
||||
private final static String NODE_USES_SDK = "uses-sdk"; //$NON-NLS-1$
|
||||
|
||||
private final static int LEVEL_MANIFEST = 0;
|
||||
private final static int LEVEL_APPLICATION = 1;
|
||||
@@ -77,6 +79,8 @@ public class AndroidManifestParser {
|
||||
private Set<String> mProcesses = null;
|
||||
/** debuggable attribute value. If null, the attribute is not present. */
|
||||
private Boolean mDebuggable = null;
|
||||
/** API level requirement. if 0 the attribute was not present. */
|
||||
private int mApiLevelRequirement = 0;
|
||||
|
||||
//--- temporary data/flags used during parsing
|
||||
private IJavaProject mJavaProject;
|
||||
@@ -142,12 +146,19 @@ public class AndroidManifestParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the debuggable attribute value or null if it is not set.
|
||||
* Returns the <code>debuggable</code> attribute value or null if it is not set.
|
||||
*/
|
||||
Boolean getDebuggable() {
|
||||
return mDebuggable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set.
|
||||
*/
|
||||
int getApiLevelRequirement() {
|
||||
return mApiLevelRequirement;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
|
||||
*/
|
||||
@@ -171,7 +182,7 @@ public class AndroidManifestParser {
|
||||
|
||||
// if we're at a valid level
|
||||
if (mValidLevel == mCurrentLevel) {
|
||||
String processName;
|
||||
String value;
|
||||
switch (mValidLevel) {
|
||||
case LEVEL_MANIFEST:
|
||||
if (NODE_MANIFEST.equals(localName)) {
|
||||
@@ -183,19 +194,28 @@ public class AndroidManifestParser {
|
||||
break;
|
||||
case LEVEL_APPLICATION:
|
||||
if (NODE_APPLICATION.equals(localName)) {
|
||||
processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
|
||||
value = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
|
||||
true /* hasNamespace */);
|
||||
if (processName != null) {
|
||||
addProcessName(processName);
|
||||
if (value != null) {
|
||||
addProcessName(value);
|
||||
}
|
||||
|
||||
String debuggable = getAttributeValue(attributes,
|
||||
ATTRIBUTE_DEBUGGABLE, true /* hasNamespace*/);
|
||||
if (debuggable != null) {
|
||||
mDebuggable = Boolean.parseBoolean(debuggable);
|
||||
value = getAttributeValue(attributes, ATTRIBUTE_DEBUGGABLE,
|
||||
true /* hasNamespace*/);
|
||||
if (value != null) {
|
||||
mDebuggable = Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
mValidLevel++;
|
||||
} else if (NODE_USES_SDK.equals(localName)) {
|
||||
value = getAttributeValue(attributes, ATTRIBUTE_MIN_SDK_VERSION,
|
||||
true /* hasNamespace */);
|
||||
|
||||
try {
|
||||
mApiLevelRequirement = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
handleError(e, -1 /* lineNumber */);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LEVEL_ACTIVITY:
|
||||
@@ -301,7 +321,7 @@ public class AndroidManifestParser {
|
||||
@Override
|
||||
public void error(SAXParseException e) throws SAXException {
|
||||
if (mMarkErrors) {
|
||||
super.error(e);
|
||||
handleError(e, e.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +331,7 @@ public class AndroidManifestParser {
|
||||
@Override
|
||||
public void fatalError(SAXParseException e) throws SAXException {
|
||||
if (mMarkErrors) {
|
||||
super.fatalError(e);
|
||||
handleError(e, e.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,6 +477,7 @@ public class AndroidManifestParser {
|
||||
private final String mLauncherActivity;
|
||||
private final String[] mProcesses;
|
||||
private final Boolean mDebuggable;
|
||||
private final int mApiLevelRequirement;
|
||||
|
||||
static {
|
||||
sParserFactory = SAXParserFactory.newInstance();
|
||||
@@ -491,7 +512,8 @@ public class AndroidManifestParser {
|
||||
|
||||
return new AndroidManifestParser(manifestHandler.getPackage(),
|
||||
manifestHandler.getActivities(), manifestHandler.getLauncherActivity(),
|
||||
manifestHandler.getProcesses(), manifestHandler.getDebuggable());
|
||||
manifestHandler.getProcesses(), manifestHandler.getDebuggable(),
|
||||
manifestHandler.getApiLevelRequirement());
|
||||
} catch (ParserConfigurationException e) {
|
||||
} catch (SAXException e) {
|
||||
} catch (IOException e) {
|
||||
@@ -530,7 +552,8 @@ public class AndroidManifestParser {
|
||||
// get the result from the handler
|
||||
return new AndroidManifestParser(manifestHandler.getPackage(),
|
||||
manifestHandler.getActivities(), manifestHandler.getLauncherActivity(),
|
||||
manifestHandler.getProcesses(), manifestHandler.getDebuggable());
|
||||
manifestHandler.getProcesses(), manifestHandler.getDebuggable(),
|
||||
manifestHandler.getApiLevelRequirement());
|
||||
}
|
||||
} catch (ParserConfigurationException e) {
|
||||
} catch (SAXException e) {
|
||||
@@ -609,6 +632,14 @@ public class AndroidManifestParser {
|
||||
return mDebuggable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set.
|
||||
*/
|
||||
public int getApiLevelRequirement() {
|
||||
return mApiLevelRequirement;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor to enforce using
|
||||
* {@link #parse(IJavaProject, XmlErrorListener, boolean, boolean)},
|
||||
@@ -619,14 +650,17 @@ public class AndroidManifestParser {
|
||||
* @param activities the list of activities parsed from the manifest.
|
||||
* @param launcherActivity the launcher activity parser from the manifest.
|
||||
* @param processes the list of custom processes declared in the manifest.
|
||||
* @param debuggable the debuggable attribute.
|
||||
* @param debuggable the debuggable attribute, or null if not set.
|
||||
* @param apiLevelRequirement the minSdkVersion attribute value or 0 if not set.
|
||||
*/
|
||||
private AndroidManifestParser(String javaPackage, String[] activities,
|
||||
String launcherActivity, String[] processes, Boolean debuggable) {
|
||||
String launcherActivity, String[] processes, Boolean debuggable,
|
||||
int apiLevelRequirement) {
|
||||
mJavaPackage = javaPackage;
|
||||
mActivities = activities;
|
||||
mLauncherActivity = launcherActivity;
|
||||
mProcesses = processes;
|
||||
mDebuggable = debuggable;
|
||||
mApiLevelRequirement = apiLevelRequirement;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.android.ide.eclipse.common.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.CommonPlugin;
|
||||
import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
@@ -108,7 +108,7 @@ public final class BaseProjectHelper {
|
||||
|
||||
return marker;
|
||||
} catch (CoreException e) {
|
||||
CommonPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
|
||||
AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
|
||||
markerId, file.getFullPath());
|
||||
}
|
||||
|
||||
@@ -117,10 +117,9 @@ public final class BaseProjectHelper {
|
||||
|
||||
/**
|
||||
* Adds a marker to a resource.
|
||||
* @param file the file to be marked
|
||||
* @param resource the file to be marked
|
||||
* @param markerId The id of the marker to add.
|
||||
* @param message the message associated with the mark
|
||||
* @param lineNumber the line number where to put the mark
|
||||
* @param severity the severity of the marker.
|
||||
* @return the IMarker that was added or null if it failed to add one.
|
||||
*/
|
||||
@@ -138,7 +137,7 @@ public final class BaseProjectHelper {
|
||||
|
||||
return marker;
|
||||
} catch (CoreException e) {
|
||||
CommonPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
|
||||
AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
|
||||
markerId, resource.getFullPath());
|
||||
}
|
||||
|
||||
@@ -65,11 +65,7 @@ public class XmlErrorHandler extends DefaultHandler {
|
||||
*/
|
||||
@Override
|
||||
public void error(SAXParseException exception) throws SAXException {
|
||||
if (mErrorListener != null) {
|
||||
mErrorListener.errorFound();
|
||||
}
|
||||
BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(),
|
||||
exception.getLineNumber(), IMarker.SEVERITY_ERROR);
|
||||
handleError(exception, exception.getLineNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,13 +73,8 @@ public class XmlErrorHandler extends DefaultHandler {
|
||||
* @param exception the parsing exception
|
||||
*/
|
||||
@Override
|
||||
public void fatalError(SAXParseException exception)
|
||||
throws SAXException {
|
||||
if (mErrorListener != null) {
|
||||
mErrorListener.errorFound();
|
||||
}
|
||||
BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(),
|
||||
exception.getLineNumber(), IMarker.SEVERITY_ERROR);
|
||||
public void fatalError(SAXParseException exception) throws SAXException {
|
||||
handleError(exception, exception.getLineNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,4 +90,23 @@ public class XmlErrorHandler extends DefaultHandler {
|
||||
protected final IFile getFile() {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a parsing error and an optional line number.
|
||||
* @param exception
|
||||
* @param lineNumber
|
||||
*/
|
||||
protected void handleError(Exception exception, int lineNumber) {
|
||||
if (mErrorListener != null) {
|
||||
mErrorListener.errorFound();
|
||||
}
|
||||
|
||||
if (lineNumber != -1) {
|
||||
BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(),
|
||||
lineNumber, IMarker.SEVERITY_ERROR);
|
||||
} else {
|
||||
BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(),
|
||||
IMarker.SEVERITY_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.ide.eclipse.common.resources;
|
||||
|
||||
import com.android.ide.eclipse.common.CommonPlugin;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo;
|
||||
@@ -106,7 +106,7 @@ public final class AttrsXmlParser {
|
||||
Document doc = getDocument();
|
||||
|
||||
if (doc == null) {
|
||||
CommonPlugin.log(IStatus.WARNING, "Failed to find %1$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(IStatus.WARNING, "Failed to find %1$s", //$NON-NLS-1$
|
||||
mOsAttrsXmlPath);
|
||||
return this;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public final class AttrsXmlParser {
|
||||
}
|
||||
|
||||
if (res == null) {
|
||||
CommonPlugin.log(IStatus.WARNING, "Failed to find a <resources> node in %1$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(IStatus.WARNING, "Failed to find a <resources> node in %1$s", //$NON-NLS-1$
|
||||
mOsAttrsXmlPath);
|
||||
return this;
|
||||
}
|
||||
@@ -189,13 +189,13 @@ public final class AttrsXmlParser {
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
mDocument = builder.parse(new File(mOsAttrsXmlPath));
|
||||
} catch (ParserConfigurationException e) {
|
||||
CommonPlugin.log(e, "Failed to create XML document builder for %1$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Failed to create XML document builder for %1$s", //$NON-NLS-1$
|
||||
mOsAttrsXmlPath);
|
||||
} catch (SAXException e) {
|
||||
CommonPlugin.log(e, "Failed to parse XML document %1$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Failed to parse XML document %1$s", //$NON-NLS-1$
|
||||
mOsAttrsXmlPath);
|
||||
} catch (IOException e) {
|
||||
CommonPlugin.log(e, "Failed to read XML document %1$s", //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Failed to read XML document %1$s", //$NON-NLS-1$
|
||||
mOsAttrsXmlPath);
|
||||
}
|
||||
}
|
||||
@@ -340,7 +340,7 @@ public final class AttrsXmlParser {
|
||||
formats.add(format);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
CommonPlugin.log(e, "Unknown format name '%s' in <attr name=\"%s\">, file '%s'.", //$NON-NLS-1$
|
||||
AdtPlugin.log(e, "Unknown format name '%s' in <attr name=\"%s\">, file '%s'.", //$NON-NLS-1$
|
||||
f, name, getOsAttrsXmlPath());
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ public final class AttrsXmlParser {
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(filter)) {
|
||||
Node nameNode = child.getAttributes().getNamedItem("name"); //$NON-NLS-1$
|
||||
if (nameNode == null) {
|
||||
CommonPlugin.log(IStatus.WARNING,
|
||||
AdtPlugin.log(IStatus.WARNING,
|
||||
"Missing name attribute in <attr name=\"%s\"><%s></attr>", //$NON-NLS-1$
|
||||
attrName, filter);
|
||||
} else {
|
||||
@@ -401,7 +401,7 @@ public final class AttrsXmlParser {
|
||||
|
||||
Node valueNode = child.getAttributes().getNamedItem("value"); //$NON-NLS-1$
|
||||
if (valueNode == null) {
|
||||
CommonPlugin.log(IStatus.WARNING,
|
||||
AdtPlugin.log(IStatus.WARNING,
|
||||
"Missing value attribute in <attr name=\"%s\"><%s name=\"%s\"></attr>", //$NON-NLS-1$
|
||||
attrName, filter, name);
|
||||
} else {
|
||||
@@ -419,7 +419,7 @@ public final class AttrsXmlParser {
|
||||
map.put(name, Integer.valueOf(i));
|
||||
|
||||
} catch(NumberFormatException e) {
|
||||
CommonPlugin.log(e,
|
||||
AdtPlugin.log(e,
|
||||
"Value in <attr name=\"%s\"><%s name=\"%s\" value=\"%s\"></attr> is not a valid decimal or hexadecimal", //$NON-NLS-1$
|
||||
attrName, filter, name, value);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package com.android.ide.eclipse.common.resources;
|
||||
*/
|
||||
public enum ResourceType {
|
||||
ANIM("anim", "Animation"), //$NON-NLS-1$
|
||||
ARRAY("array", "Array"), //$NON-NLS-1$
|
||||
ARRAY("array", "Array", "string-array", "integer-array"), //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
ATTR("attr", "Attr"), //$NON-NLS-1$
|
||||
COLOR("color", "Color"), //$NON-NLS-1$
|
||||
DIMEN("dimen", "Dimension"), //$NON-NLS-1$
|
||||
@@ -35,12 +35,14 @@ public enum ResourceType {
|
||||
STYLEABLE("styleable", "Styleable"), //$NON-NLS-1$
|
||||
XML("xml", "XML"); //$NON-NLS-1$
|
||||
|
||||
private String mName;
|
||||
private String mDisplayName;
|
||||
private final String mName;
|
||||
private final String mDisplayName;
|
||||
private final String[] mAlternateXmlNames;
|
||||
|
||||
ResourceType(String name, String displayName) {
|
||||
ResourceType(String name, String displayName, String... alternateXmlNames) {
|
||||
mName = name;
|
||||
mDisplayName = displayName;
|
||||
mAlternateXmlNames = alternateXmlNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +68,13 @@ public enum ResourceType {
|
||||
for (ResourceType rType : values()) {
|
||||
if (rType.mName.equals(name)) {
|
||||
return rType;
|
||||
} else if (rType.mAlternateXmlNames != null) {
|
||||
// if there are alternate Xml Names, we test those too
|
||||
for (String alternate : rType.mAlternateXmlNames) {
|
||||
if (alternate.equals(name)) {
|
||||
return rType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.android.ide.eclipse.editors;
|
||||
|
||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider;
|
||||
import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextValueDescriptor;
|
||||
@@ -80,15 +81,20 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
protected final static String ROOT_ELEMENT = "";
|
||||
|
||||
/** Descriptor of the root of the XML hierarchy. This a "fake" ElementDescriptor which
|
||||
* is used to list all the possible roots given by actual implementations. */
|
||||
* is used to list all the possible roots given by actual implementations.
|
||||
* DO NOT USE DIRECTLY. Call {@link #getRootDescriptor()} instead. */
|
||||
private ElementDescriptor mRootDescriptor;
|
||||
|
||||
private final int mDescriptorId;
|
||||
|
||||
private AndroidEditor mEditor;
|
||||
|
||||
/**
|
||||
* Constructor for AndroidContentAssist
|
||||
* @param rootElementDescriptors The valid root elements of the XML hierarchy
|
||||
*/
|
||||
public AndroidContentAssist(ElementDescriptor[] rootElementDescriptors) {
|
||||
mRootDescriptor = new ElementDescriptor("", rootElementDescriptors);
|
||||
public AndroidContentAssist(int descriptorId) {
|
||||
mDescriptorId = descriptorId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,8 +110,11 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
*/
|
||||
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
|
||||
|
||||
AndroidEditor editor = getAndroidEditor(viewer);
|
||||
UiElementNode rootUiNode = editor.getUiRootNode();
|
||||
if (mEditor == null) {
|
||||
mEditor = getAndroidEditor(viewer);
|
||||
}
|
||||
|
||||
UiElementNode rootUiNode = mEditor.getUiRootNode();
|
||||
|
||||
Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor
|
||||
or String or null */
|
||||
@@ -245,7 +254,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
if (current_node.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
|
||||
grandparent = getDescriptor(current_node.getParentNode().getNodeName());
|
||||
} else if (current_node.getParentNode().getNodeType() == Node.DOCUMENT_NODE) {
|
||||
grandparent = mRootDescriptor;
|
||||
grandparent = getRootDescriptor();
|
||||
}
|
||||
if (grandparent != null) {
|
||||
for (ElementDescriptor e : grandparent.getChildren()) {
|
||||
@@ -339,8 +348,11 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
greatGrandParentName = greatGrandParent.getLocalName();
|
||||
}
|
||||
}
|
||||
choices = FrameworkResourceManager.getInstance().getValues(
|
||||
parent, attrInfo.name, greatGrandParentName);
|
||||
|
||||
AndroidTargetData data = mEditor.getTargetData();
|
||||
if (data != null) {
|
||||
choices = data.getAttributeValues(parent, attrInfo.name, greatGrandParentName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Editing an attribute's name... Get attributes valid for the parent node.
|
||||
@@ -378,7 +390,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
} else if (parent_node.getNodeType() == Node.DOCUMENT_NODE) {
|
||||
// We're editing a text node at the first level (i.e. root node).
|
||||
// Limit content assist to the only valid root elements.
|
||||
choices = mRootDescriptor.getChildren();
|
||||
choices = getRootDescriptor().getChildren();
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
@@ -509,7 +521,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
* is returned.
|
||||
*/
|
||||
private ElementDescriptor getDescriptor(String nodeName) {
|
||||
return mRootDescriptor.findChildrenDescriptor(nodeName, true /* recursive */);
|
||||
return getRootDescriptor().findChildrenDescriptor(nodeName, true /* recursive */);
|
||||
}
|
||||
|
||||
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
|
||||
@@ -539,8 +551,8 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Heuristically extracts the prefix used for determining template relevance
|
||||
* from the viewer's document. The default implementation returns the String from
|
||||
@@ -709,6 +721,26 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes (if needed) and returns the root descriptor.
|
||||
* @return
|
||||
*/
|
||||
private ElementDescriptor getRootDescriptor() {
|
||||
if (mRootDescriptor == null) {
|
||||
AndroidTargetData data = mEditor.getTargetData();
|
||||
if (data != null) {
|
||||
IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
|
||||
|
||||
if (descriptorProvider != null) {
|
||||
mRootDescriptor = new ElementDescriptor("",
|
||||
descriptorProvider.getRootElementDescriptors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mRootDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active {@link AndroidEditor} matching this source viewer.
|
||||
*/
|
||||
@@ -729,5 +761,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package com.android.ide.eclipse.editors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceChangeEvent;
|
||||
import org.eclipse.core.resources.IResourceChangeListener;
|
||||
@@ -29,15 +33,18 @@ import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.QualifiedName;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.dialogs.ErrorDialog;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.ui.IActionBars;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.IEditorSite;
|
||||
import org.eclipse.ui.IFileEditorInput;
|
||||
import org.eclipse.ui.IWorkbenchPage;
|
||||
import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.actions.ActionFactory;
|
||||
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.editor.FormEditor;
|
||||
@@ -71,7 +78,7 @@ import java.net.URL;
|
||||
* source editor. This can be a no-op if desired.
|
||||
*/
|
||||
public abstract class AndroidEditor extends FormEditor implements IResourceChangeListener {
|
||||
|
||||
|
||||
/** Preference name for the current page of this file */
|
||||
private static final String PREF_CURRENT_PAGE = "_current_page";
|
||||
|
||||
@@ -87,9 +94,11 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
/** Page index of the text editor (always the last page) */
|
||||
private int mTextPageIndex;
|
||||
/** The text editor */
|
||||
private StructuredTextEditor mEditor;
|
||||
private StructuredTextEditor mTextEditor;
|
||||
/** Listener for the XML model from the StructuredEditor */
|
||||
private XmlModelStateListener mXmlModelStateListener;
|
||||
/** Listener to update the root node if the resource framework changes */
|
||||
private Runnable mResourceRefreshListener;
|
||||
|
||||
/**
|
||||
* Creates a form editor.
|
||||
@@ -97,6 +106,16 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
public AndroidEditor() {
|
||||
super();
|
||||
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
|
||||
|
||||
mResourceRefreshListener = new Runnable() {
|
||||
public void run() {
|
||||
commitPages(false /* onSave */);
|
||||
|
||||
// recreate the ui root node always
|
||||
initUiRootNode(true /*force*/);
|
||||
}
|
||||
};
|
||||
AdtPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
|
||||
}
|
||||
|
||||
// ---- Abstract Methods ----
|
||||
@@ -113,6 +132,12 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
* Derived classes must implement this to add their own specific tabs.
|
||||
*/
|
||||
abstract protected void createFormPages();
|
||||
|
||||
/**
|
||||
* Creates the initial UI Root Node, including the known mandatory elements.
|
||||
* @param force if true, a new UiManifestNode is recreated even if it already exists.
|
||||
*/
|
||||
abstract protected void initUiRootNode(boolean force);
|
||||
|
||||
/**
|
||||
* Subclasses should override this method to process the new XML Model, which XML
|
||||
@@ -143,6 +168,26 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
protected void createAndroidPages() {
|
||||
createFormPages();
|
||||
createTextEditor();
|
||||
|
||||
createUndoRedoActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates undo redo actions for the editor site (so that it works for any page of this
|
||||
* multi-page editor) by re-using the actions defined by the {@link StructuredTextEditor}
|
||||
* (aka the XML text editor.)
|
||||
*/
|
||||
private void createUndoRedoActions() {
|
||||
IActionBars bars = getEditorSite().getActionBars();
|
||||
if (bars != null) {
|
||||
IAction action = mTextEditor.getAction(ActionFactory.UNDO.getId());
|
||||
bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), action);
|
||||
|
||||
action = mTextEditor.getAction(ActionFactory.REDO.getId());
|
||||
bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action);
|
||||
|
||||
bars.updateActionBars();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +200,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
if (getEditorInput() instanceof IFileEditorInput) {
|
||||
IFile file = ((IFileEditorInput) getEditorInput()).getFile();
|
||||
|
||||
QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
|
||||
getClass().getSimpleName() + PREF_CURRENT_PAGE);
|
||||
String pageId;
|
||||
try {
|
||||
@@ -177,7 +222,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
// AssertionError from setActivePage when the index is out of bounds.
|
||||
// Generally speaking we just want to ignore any exception and fall back on the
|
||||
// first page rather than crash the editor load. Logging the error is enough.
|
||||
EditorsPlugin.log(e, "Selecting page '%s' in AndroidEditor failed", defaultPageId);
|
||||
AdtPlugin.log(e, "Selecting page '%s' in AndroidEditor failed", defaultPageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +269,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
if (getEditorInput() instanceof IFileEditorInput) {
|
||||
IFile file = ((IFileEditorInput) getEditorInput()).getFile();
|
||||
|
||||
QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
|
||||
getClass().getSimpleName() + PREF_CURRENT_PAGE);
|
||||
try {
|
||||
file.setPersistentProperty(qname, Integer.toString(newPageIndex));
|
||||
@@ -248,10 +293,10 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
|
||||
.getPages();
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
if (((FileEditorInput)mEditor.getEditorInput())
|
||||
if (((FileEditorInput)mTextEditor.getEditorInput())
|
||||
.getFile().getProject().equals(
|
||||
event.getResource())) {
|
||||
IEditorPart editorPart = pages[i].findEditor(mEditor
|
||||
IEditorPart editorPart = pages[i].findEditor(mTextEditor
|
||||
.getEditorInput());
|
||||
pages[i].closeEditor(editorPart, true);
|
||||
}
|
||||
@@ -294,6 +339,12 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
}
|
||||
}
|
||||
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
|
||||
|
||||
if (mResourceRefreshListener != null) {
|
||||
AdtPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
|
||||
mResourceRefreshListener = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -447,13 +498,13 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
*/
|
||||
private void createTextEditor() {
|
||||
try {
|
||||
mEditor = new StructuredTextEditor();
|
||||
int index = addPage(mEditor, getEditorInput());
|
||||
mTextEditor = new StructuredTextEditor();
|
||||
int index = addPage(mTextEditor, getEditorInput());
|
||||
mTextPageIndex = index;
|
||||
setPageText(index, mEditor.getTitle());
|
||||
setPageText(index, mTextEditor.getTitle());
|
||||
|
||||
if (!(mEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) {
|
||||
Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
if (!(mTextEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) {
|
||||
Status status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
|
||||
"Error opening the Android XML editor. Is the document an XML file?");
|
||||
throw new RuntimeException("Android XML Editor Error", new CoreException(status));
|
||||
}
|
||||
@@ -464,6 +515,8 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
mXmlModelStateListener = new XmlModelStateListener();
|
||||
xml_model.addModelStateListener(mXmlModelStateListener);
|
||||
mXmlModelStateListener.modelChanged(xml_model);
|
||||
} catch (Exception e) {
|
||||
AdtPlugin.log(e, "Error while loading editor"); //$NON-NLS-1$
|
||||
} finally {
|
||||
xml_model.releaseFromRead();
|
||||
}
|
||||
@@ -478,11 +531,11 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
* Returns the ISourceViewer associated with the Structured Text editor.
|
||||
*/
|
||||
public final ISourceViewer getStructuredSourceViewer() {
|
||||
if (mEditor != null) {
|
||||
if (mTextEditor != null) {
|
||||
// We can't access mEditor.getSourceViewer() because it is protected,
|
||||
// however getTextViewer simply returns the SourceViewer casted, so we
|
||||
// can use it instead.
|
||||
return mEditor.getTextViewer();
|
||||
return mTextEditor.getTextViewer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -492,8 +545,8 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
* Editor) or null if not available.
|
||||
*/
|
||||
public final IStructuredDocument getStructuredDocument() {
|
||||
if (mEditor != null && mEditor.getTextViewer() != null) {
|
||||
return (IStructuredDocument) mEditor.getTextViewer().getDocument();
|
||||
if (mTextEditor != null && mTextEditor.getTextViewer() != null) {
|
||||
return (IStructuredDocument) mTextEditor.getTextViewer().getDocument();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -539,14 +592,17 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
/**
|
||||
* Helper class to perform edits on the XML model whilst making sure the
|
||||
* model has been prepared to be changed.
|
||||
* <p/>
|
||||
* It first gets a model for edition using {@link #getModelForEdit()},
|
||||
* then calls {@link IStructuredModel#aboutToChangeModel()},
|
||||
* then performs the requested action
|
||||
* and finally calls {@link IStructuredModel#changedModel()}
|
||||
* and {@link IStructuredModel#releaseFromEdit()}.
|
||||
* <p/>
|
||||
* The method is synchronous. As soon as the {@link IStructuredModel#changedModel()} method
|
||||
* is called, XML model listeners will be triggered.
|
||||
*
|
||||
* It first gets a model for edition, then calls aboutToChangeModel, then performs the
|
||||
* requested action and finally calls changedModel and releaseFromEdit.
|
||||
*
|
||||
* The method is synchronous. As soon as the changedModel method is called, XML model
|
||||
* listeners will be triggered.
|
||||
*
|
||||
* @param edit_action Something that will change
|
||||
* @param edit_action Something that will change the XML.
|
||||
*/
|
||||
public final void editXmlModel(Runnable edit_action) {
|
||||
IStructuredModel model = getModelForEdit();
|
||||
@@ -560,12 +616,82 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an "undo recording" session. This is managed by the underlying undo manager
|
||||
* associated to the structured XML model.
|
||||
* <p/>
|
||||
* There <em>must</em> be a corresponding call to {@link #endUndoRecording()}.
|
||||
* <p/>
|
||||
* beginUndoRecording/endUndoRecording calls can be nested (inner calls are ignored, only one
|
||||
* undo operation is recorded.)
|
||||
*
|
||||
* @param label The label for the undo operation. Can be null but we should really try to put
|
||||
* something meaningful if possible.
|
||||
* @return True if the undo recording actually started, false if any kind of error occured.
|
||||
* {@link #endUndoRecording()} should only be called if True is returned.
|
||||
*/
|
||||
private final boolean beginUndoRecording(String label) {
|
||||
IStructuredDocument document = getStructuredDocument();
|
||||
if (document != null) {
|
||||
IModelManager mm = StructuredModelManager.getModelManager();
|
||||
if (mm != null) {
|
||||
IStructuredModel model = mm.getModelForEdit(document);
|
||||
if (model != null) {
|
||||
model.beginRecording(this, label);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an "undo recording" session.
|
||||
* <p/>
|
||||
* This is the counterpart call to {@link #beginUndoRecording(String)} and should only be
|
||||
* used if the initial call returned true.
|
||||
*/
|
||||
private final void endUndoRecording() {
|
||||
IStructuredDocument document = getStructuredDocument();
|
||||
if (document != null) {
|
||||
IModelManager mm = StructuredModelManager.getModelManager();
|
||||
if (mm != null) {
|
||||
IStructuredModel model = mm.getModelForEdit(document);
|
||||
if (model != null) {
|
||||
model.endRecording(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an "undo recording" session by calling the undoableAction runnable
|
||||
* using {@link #beginUndoRecording(String)} and {@link #endUndoRecording()}.
|
||||
* <p>
|
||||
* You can nest several calls to {@link #wrapUndoRecording(String, Runnable)}, only one
|
||||
* recording session will be created.
|
||||
*
|
||||
* @param label The label for the undo operation. Can be null. Ideally we should really try
|
||||
* to put something meaningful if possible.
|
||||
*/
|
||||
public void wrapUndoRecording(String label, Runnable undoableAction) {
|
||||
boolean recording = false;
|
||||
try {
|
||||
recording = beginUndoRecording(label);
|
||||
undoableAction.run();
|
||||
} finally {
|
||||
if (recording) {
|
||||
endUndoRecording();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML {@link Document} or null if we can't get it
|
||||
*/
|
||||
protected final Document getXmlDocument(IStructuredModel model) {
|
||||
if (model == null) {
|
||||
EditorsPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$
|
||||
AdtPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -576,6 +702,45 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IProject} for the edited file.
|
||||
*/
|
||||
public IProject getProject() {
|
||||
if (mTextEditor != null) {
|
||||
IEditorInput input = mTextEditor.getEditorInput();
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput)input;
|
||||
IFile inputFile = fileInput.getFile();
|
||||
|
||||
if (inputFile != null) {
|
||||
return inputFile.getProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PlatformData} for the edited file.
|
||||
*/
|
||||
public AndroidTargetData getTargetData() {
|
||||
IProject project = getProject();
|
||||
if (project != null) {
|
||||
Sdk currentSdk = Sdk.getCurrent();
|
||||
if (currentSdk != null) {
|
||||
IAndroidTarget target = currentSdk.getTarget(project);
|
||||
|
||||
if (target != null) {
|
||||
return currentSdk.getTargetData(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listen to changes in the underlying XML model in the structured editor.
|
||||
*/
|
||||