Initial Contribution
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="lib" path="jarutils.jar"/>
|
||||
<classpathentry kind="lib" path="androidprefs.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
28
tools/eclipse/plugins/com.android.ide.eclipse.adt/.project
Normal file
28
tools/eclipse/plugins/com.android.ide.eclipse.adt/.project
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>adt</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,36 @@
|
||||
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-ClassPath: .,
|
||||
jarutils.jar,
|
||||
androidprefs.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,
|
||||
org.eclipse.core.runtime,
|
||||
org.eclipse.core.resources,
|
||||
org.eclipse.debug.core,
|
||||
org.eclipse.debug.ui,
|
||||
org.eclipse.jdt,
|
||||
org.eclipse.ant.core,
|
||||
org.eclipse.jdt.core,
|
||||
org.eclipse.jdt.ui,
|
||||
org.eclipse.jdt.launching,
|
||||
org.eclipse.jface.text,
|
||||
org.eclipse.ui.editors,
|
||||
org.eclipse.ui.workbench.texteditor,
|
||||
org.eclipse.ui.console,
|
||||
org.eclipse.core.filesystem,
|
||||
org.eclipse.ui,
|
||||
org.eclipse.ui.ide,
|
||||
org.eclipse.ui.forms
|
||||
Eclipse-LazyStart: true
|
||||
Export-Package: 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"
|
||||
|
||||
|
||||
224
tools/eclipse/plugins/com.android.ide.eclipse.adt/NOTICE
Normal file
224
tools/eclipse/plugins/com.android.ide.eclipse.adt/NOTICE
Normal file
@@ -0,0 +1,224 @@
|
||||
*Eclipse Public License - v 1.0*
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
|
||||
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
|
||||
THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
*1. DEFINITIONS*
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and
|
||||
documentation distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
|
||||
i) changes to the Program, and
|
||||
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and
|
||||
are distributed by that particular Contributor. A Contribution
|
||||
'originates' from a Contributor if it was added to the Program by such
|
||||
Contributor itself or anyone acting on such Contributor's behalf.
|
||||
Contributions do not include additions to the Program which: (i) are
|
||||
separate modules of software distributed in conjunction with the Program
|
||||
under their own license agreement, and (ii) are not derivative works of
|
||||
the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents " mean patent claims licensable by a Contributor which
|
||||
are necessarily infringed by the use or sale of its Contribution alone
|
||||
or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this
|
||||
Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
|
||||
*2. GRANT OF RIGHTS*
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free copyright
|
||||
license to reproduce, prepare derivative works of, publicly display,
|
||||
publicly perform, distribute and sublicense the Contribution of such
|
||||
Contributor, if any, and such derivative works, in source code and
|
||||
object code form.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free patent license
|
||||
under Licensed Patents to make, use, sell, offer to sell, import and
|
||||
otherwise transfer the Contribution of such Contributor, if any, in
|
||||
source code and object code form. This patent license shall apply to the
|
||||
combination of the Contribution and the Program if, at the time the
|
||||
Contribution is added by the Contributor, such addition of the
|
||||
Contribution causes such combination to be covered by the Licensed
|
||||
Patents. The patent license shall not apply to any other combinations
|
||||
which include the Contribution. No hardware per se is licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the
|
||||
licenses to its Contributions set forth herein, no assurances are
|
||||
provided by any Contributor that the Program does not infringe the
|
||||
patent or other intellectual property rights of any other entity. Each
|
||||
Contributor disclaims any liability to Recipient for claims brought by
|
||||
any other entity based on infringement of intellectual property rights
|
||||
or otherwise. As a condition to exercising the rights and licenses
|
||||
granted hereunder, each Recipient hereby assumes sole responsibility to
|
||||
secure any other intellectual property rights needed, if any. For
|
||||
example, if a third party patent license is required to allow Recipient
|
||||
to distribute the Program, it is Recipient's responsibility to acquire
|
||||
that license before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright
|
||||
license set forth in this Agreement.
|
||||
|
||||
*3. REQUIREMENTS*
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form
|
||||
under its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
|
||||
b) its license agreement:
|
||||
|
||||
i) effectively disclaims on behalf of all Contributors all warranties
|
||||
and conditions, express and implied, including warranties or conditions
|
||||
of title and non-infringement, and implied warranties or conditions of
|
||||
merchantability and fitness for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and
|
||||
consequential damages, such as lost profits;
|
||||
|
||||
iii) states that any provisions which differ from this Agreement are
|
||||
offered by that Contributor alone and not by any other party; and
|
||||
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable
|
||||
manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
|
||||
Contributors may not remove or alter any copyright notices contained
|
||||
within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its
|
||||
Contribution, if any, in a manner that reasonably allows subsequent
|
||||
Recipients to identify the originator of the Contribution.
|
||||
|
||||
*4. COMMERCIAL DISTRIBUTION*
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities
|
||||
with respect to end users, business partners and the like. While this
|
||||
license is intended to facilitate the commercial use of the Program, the
|
||||
Contributor who includes the Program in a commercial product offering
|
||||
should do so in a manner which does not create potential liability for
|
||||
other Contributors. Therefore, if a Contributor includes the Program in
|
||||
a commercial product offering, such Contributor ("Commercial
|
||||
Contributor") hereby agrees to defend and indemnify every other
|
||||
Contributor ("Indemnified Contributor") against any losses, damages and
|
||||
costs (collectively "Losses") arising from claims, lawsuits and other
|
||||
legal actions brought by a third party against the Indemnified
|
||||
Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the
|
||||
Program in a commercial product offering. The obligations in this
|
||||
section do not apply to any claims or Losses relating to any actual or
|
||||
alleged intellectual property infringement. In order to qualify, an
|
||||
Indemnified Contributor must: a) promptly notify the Commercial
|
||||
Contributor in writing of such claim, and b) allow the Commercial
|
||||
Contributor to control, and cooperate with the Commercial Contributor
|
||||
in, the defense and any related settlement negotiations. The Indemnified
|
||||
Contributor may participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial
|
||||
product offering, Product X. That Contributor is then a Commercial
|
||||
Contributor. If that Commercial Contributor then makes performance
|
||||
claims, or offers warranties related to Product X, those performance
|
||||
claims and warranties are such Commercial Contributor's responsibility
|
||||
alone. Under this section, the Commercial Contributor would have to
|
||||
defend claims against the other Contributors related to those
|
||||
performance claims and warranties, and if a court requires any other
|
||||
Contributor to pay any damages as a result, the Commercial Contributor
|
||||
must pay those damages.
|
||||
|
||||
*5. NO WARRANTY*
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
|
||||
ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
|
||||
OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
|
||||
A PARTICULAR PURPOSE. Each Recipient is solely responsible for
|
||||
determining the appropriateness of using and distributing the Program
|
||||
and assumes all risks associated with its exercise of rights under this
|
||||
Agreement , including but not limited to the risks and costs of program
|
||||
errors, compliance with applicable laws, damage to or loss of data,
|
||||
programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
*6. DISCLAIMER OF LIABILITY*
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
|
||||
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
|
||||
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
|
||||
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
|
||||
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
*7. GENERAL*
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of
|
||||
the remainder of the terms of this Agreement, and without further action
|
||||
by the parties hereto, such provision shall be reformed to the minimum
|
||||
extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including
|
||||
a cross-claim or counterclaim in a lawsuit) alleging that the Program
|
||||
itself (excluding combinations of the Program with other software or
|
||||
hardware) infringes such Recipient's patent(s), then such Recipient's
|
||||
rights granted under Section 2(b) shall terminate as of the date such
|
||||
litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails
|
||||
to comply with any of the material terms or conditions of this Agreement
|
||||
and does not cure such failure in a reasonable period of time after
|
||||
becoming aware of such noncompliance. If all Recipient's rights under
|
||||
this Agreement terminate, Recipient agrees to cease use and distribution
|
||||
of the Program as soon as reasonably practicable. However, Recipient's
|
||||
obligations under this Agreement and any licenses granted by Recipient
|
||||
relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement,
|
||||
but in order to avoid inconsistency the Agreement is copyrighted and may
|
||||
only be modified in the following manner. The Agreement Steward reserves
|
||||
the right to publish new versions (including revisions) of this
|
||||
Agreement from time to time. No one other than the Agreement Steward has
|
||||
the right to modify this Agreement. The Eclipse Foundation is the
|
||||
initial Agreement Steward. The Eclipse Foundation may assign the
|
||||
responsibility to serve as the Agreement Steward to a suitable separate
|
||||
entity. Each new version of the Agreement will be given a distinguishing
|
||||
version number. The Program (including Contributions) may always be
|
||||
distributed subject to the version of the Agreement under which it was
|
||||
received. In addition, after a new version of the Agreement is
|
||||
published, Contributor may elect to distribute the Program (including
|
||||
its Contributions) under the new version. Except as expressly stated in
|
||||
Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
|
||||
to the intellectual property of any Contributor under this Agreement,
|
||||
whether expressly, by implication, estoppel or otherwise. All rights in
|
||||
the Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to
|
||||
this Agreement will bring a legal action under this Agreement more than
|
||||
one year after the cause of action arose. Each party waives its rights
|
||||
to a jury trial in any resulting litigation.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
featureImage=icons/android_32X32.jpg
|
||||
@@ -0,0 +1,10 @@
|
||||
bin.includes = plugin.xml,\
|
||||
META-INF/,\
|
||||
icons/,\
|
||||
.,\
|
||||
templates/,\
|
||||
about.ini,\
|
||||
jarutils.jar,\
|
||||
androidprefs.jar
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 146 B |
Binary file not shown.
|
After Width: | Height: | Size: 308 B |
308
tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
Normal file
308
tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
Normal file
@@ -0,0 +1,308 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension
|
||||
id="ResourceManagerBuilder"
|
||||
name="Android Resource Manager"
|
||||
point="org.eclipse.core.resources.builders">
|
||||
<builder
|
||||
hasNature="true">
|
||||
<run class="com.android.ide.eclipse.adt.build.ResourceManagerBuilder"/>
|
||||
</builder>
|
||||
</extension>
|
||||
<extension
|
||||
id="PreCompilerBuilder"
|
||||
name="Android Pre Compiler"
|
||||
point="org.eclipse.core.resources.builders">
|
||||
<builder
|
||||
hasNature="true">
|
||||
<run class="com.android.ide.eclipse.adt.build.PreCompilerBuilder"/>
|
||||
</builder>
|
||||
</extension>
|
||||
<extension
|
||||
id="ApkBuilder"
|
||||
name="Android Package Builder"
|
||||
point="org.eclipse.core.resources.builders">
|
||||
<builder
|
||||
hasNature="true">
|
||||
<run class="com.android.ide.eclipse.adt.build.ApkBuilder"/>
|
||||
</builder>
|
||||
</extension>
|
||||
<extension
|
||||
id="AndroidNature"
|
||||
name="AndroidNature"
|
||||
point="org.eclipse.core.resources.natures">
|
||||
<runtime>
|
||||
<run class="com.android.ide.eclipse.adt.project.AndroidNature"/>
|
||||
</runtime>
|
||||
<builder id="com.android.ide.eclipse.adt.ResourceManagerBuilder"/>
|
||||
<builder id="com.android.ide.eclipse.adt.PreCompilerBuilder"/>
|
||||
<builder id="com.android.ide.eclipse.adt.ApkBuilder"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.newWizards">
|
||||
<category
|
||||
id="com.android.ide.eclipse.wizards.category"
|
||||
name="Android"/>
|
||||
<wizard
|
||||
canFinishEarly="false"
|
||||
category="com.android.ide.eclipse.wizards.category"
|
||||
class="com.android.ide.eclipse.adt.project.internal.NewProjectWizard"
|
||||
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
|
||||
hasPages="true"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.adt.project.NewProjectWizard"
|
||||
name="Android Project"
|
||||
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
|
||||
project="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.debug.core.launchConfigurationTypes">
|
||||
<launchConfigurationType
|
||||
delegate="com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate"
|
||||
delegateDescription="The Android Application Launcher supports running and debugging remote Android applications on devices or emulators."
|
||||
delegateName="Android Launcher"
|
||||
id="com.android.ide.eclipse.adt.debug.LaunchConfigType"
|
||||
modes="debug, run"
|
||||
name="Android Application"
|
||||
public="true"
|
||||
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
|
||||
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
|
||||
</launchConfigurationType>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.debug.ui.launchConfigurationTypeImages">
|
||||
<launchConfigurationTypeImage
|
||||
configTypeID="com.android.ide.eclipse.adt.debug.LaunchConfigType"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.adt.debug.LaunchConfigTypeImage"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.debug.ui.launchConfigurationTabGroups">
|
||||
<launchConfigurationTabGroup
|
||||
class="com.android.ide.eclipse.adt.debug.ui.LaunchConfigTabGroup"
|
||||
description="Android Application"
|
||||
id="com.android.ide.eclipse.adt.debug.LaunchConfigTabGroup"
|
||||
type="com.android.ide.eclipse.adt.debug.LaunchConfigType"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.debug.ui.launchShortcuts">
|
||||
<shortcut
|
||||
category="com.android.ide.eclipse.adt.debug.LaunchConfigType"
|
||||
class="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut"
|
||||
label="Android Application"
|
||||
modes="debug, run">
|
||||
<contextualLaunch>
|
||||
<enablement>
|
||||
<with variable="selection">
|
||||
<count value="1"/>
|
||||
<iterate>
|
||||
<and>
|
||||
<test property="org.eclipse.jdt.launching.isContainer"/>
|
||||
<test property="org.eclipse.jdt.launching.hasProjectNature" args="com.android.ide.eclipse.adt.AndroidNature"/>
|
||||
</and>
|
||||
</iterate>
|
||||
</with>
|
||||
</enablement>
|
||||
</contextualLaunch>
|
||||
<perspective id="org.eclipse.jdt.ui.JavaPerspective"/>
|
||||
<perspective id="org.eclipse.debug.ui.DebugPerspective"/>
|
||||
<description
|
||||
description="Runs an Android Application"
|
||||
mode="run">
|
||||
</description>
|
||||
<description
|
||||
description="Debugs an Android Application"
|
||||
mode="debug">
|
||||
</description>
|
||||
</shortcut>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.popupMenus">
|
||||
<objectContribution
|
||||
id="com.android.ide.eclipse.adt.contribution1"
|
||||
nameFilter="*"
|
||||
objectClass="org.eclipse.core.resources.IProject"
|
||||
adaptable="true">
|
||||
<menu
|
||||
id="com.android.ide.eclipse.adt.AndroidTools"
|
||||
label="Android Tools"
|
||||
path="additions">
|
||||
<separator name="group1"/>
|
||||
</menu>
|
||||
<visibility>
|
||||
<not>
|
||||
<or>
|
||||
<objectState
|
||||
name="projectNature"
|
||||
value="com.android.ide.eclipse.adt.AndroidNature"/>
|
||||
<objectState
|
||||
name="open"
|
||||
value="false"/>
|
||||
</or>
|
||||
</not>
|
||||
</visibility>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.ConvertToAndroidAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.ConvertToAndroidAction"
|
||||
label="Convert To Android Project"
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1"/>
|
||||
</objectContribution>
|
||||
<objectContribution
|
||||
id="com.android.ide.eclipse.adt.contribution2"
|
||||
nameFilter="*"
|
||||
objectClass="org.eclipse.core.resources.IProject"
|
||||
adaptable="true">
|
||||
<menu
|
||||
id="com.android.ide.eclipse.adt.AndroidTools"
|
||||
label="Android Tools"
|
||||
path="additions">
|
||||
<separator name="group1"/>
|
||||
<separator name="group2"/>
|
||||
</menu>
|
||||
<filter
|
||||
name="projectNature"
|
||||
value="com.android.ide.eclipse.adt.AndroidNature">
|
||||
</filter>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.CreateAidlImportAction"
|
||||
enablesFor="1"
|
||||
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.ExportAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.project.ExportAction"
|
||||
label="Export Unsigned Application Package..."
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group2"/>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.ExportWizardAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.project.ExportWizardAction"
|
||||
label="Export Application..."
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group2"/>
|
||||
<action
|
||||
class="com.android.ide.eclipse.adt.project.FixProjectAction"
|
||||
enablesFor="1"
|
||||
id="com.android.ide.eclipse.adt.project.FixProjectAction"
|
||||
label="Fix Project Properties"
|
||||
menubarPath="com.android.ide.eclipse.adt.AndroidTools/group3"/>
|
||||
</objectContribution>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.preferencePages">
|
||||
<page
|
||||
class="com.android.ide.eclipse.adt.preferences.AndroidPreferencePage"
|
||||
id="com.android.ide.eclipse.preferences.main"
|
||||
name="Android"/>
|
||||
<page
|
||||
category="com.android.ide.eclipse.preferences.main"
|
||||
class="com.android.ide.eclipse.adt.preferences.BuildPreferencePage"
|
||||
id="com.android.ide.eclipse.adt.preferences.BuildPreferencePage"
|
||||
name="Build"/>
|
||||
<page
|
||||
category="com.android.ide.eclipse.preferences.main"
|
||||
class="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage"
|
||||
id="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage"
|
||||
name="Launch"/>
|
||||
</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"
|
||||
point="org.eclipse.core.resources.markers">
|
||||
<super type="org.eclipse.core.resources.problemmarker"/>
|
||||
<persistent value="true"/>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.perspectiveExtensions">
|
||||
<perspectiveExtension targetID="org.eclipse.jdt.ui.JavaPerspective">
|
||||
<newWizardShortcut id="com.android.ide.eclipse.adt.project.NewProjectWizard" />
|
||||
<newWizardShortcut
|
||||
id="com.android.ide.eclipse.adt.wizards.NewXmlFileWizard">
|
||||
</newWizardShortcut>
|
||||
</perspectiveExtension>
|
||||
<perspectiveExtension targetID="org.eclipse.debug.ui.DebugPerspective">
|
||||
<viewShortcut id="com.android.ide.eclipse.ddms.views.LogCatView"/>
|
||||
<viewShortcut id="com.android.ide.eclipse.ddms.views.DeviceView"/>
|
||||
</perspectiveExtension>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.ide.projectNatureImages">
|
||||
<image
|
||||
icon="icons/android_project.png"
|
||||
id="com.android.ide.eclipse.adt.AndroidNature.image"
|
||||
natureId="com.android.ide.eclipse.adt.AndroidNature">
|
||||
</image>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.jdt.core.classpathContainerInitializer">
|
||||
<classpathContainerInitializer
|
||||
class="com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer"
|
||||
id="com.android.ide.eclipse.adt.project.AndroidClasspathContainerInitializer">
|
||||
</classpathContainerInitializer>
|
||||
<classpathContainerInitializer
|
||||
class="com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer"
|
||||
id="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK">
|
||||
</classpathContainerInitializer>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.exportWizards">
|
||||
<category
|
||||
id="com.android.ide.eclipse.wizards.category"
|
||||
name="Android">
|
||||
</category>
|
||||
<wizard
|
||||
category="com.android.ide.eclipse.wizards.category"
|
||||
class="com.android.ide.eclipse.adt.project.export.ExportWizard"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.adt.project.ExportWizard"
|
||||
name="Export Android Application">
|
||||
</wizard>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.commands">
|
||||
<command
|
||||
name="Debug Android Application"
|
||||
description="Debug Android Application"
|
||||
categoryId="org.eclipse.debug.ui.category.run"
|
||||
id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.debug">
|
||||
</command>
|
||||
<command
|
||||
name="Run Android Application"
|
||||
description="Run Android Application"
|
||||
categoryId="org.eclipse.debug.ui.category.run"
|
||||
id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.run">
|
||||
</command>
|
||||
<keyBinding
|
||||
keySequence="M3+M2+A D"
|
||||
contextId="org.eclipse.ui.globalScope"
|
||||
commandId="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.debug"
|
||||
keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration">
|
||||
</keyBinding>
|
||||
<keyBinding
|
||||
keySequence="M3+M2+A R"
|
||||
contextId="org.eclipse.ui.globalScope"
|
||||
commandId="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.run"
|
||||
keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration">
|
||||
</keyBinding>
|
||||
</extension>
|
||||
</plugin>
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/**
|
||||
* Constant definition class.<br>
|
||||
* <br>
|
||||
* Most constants have a prefix defining the content.
|
||||
* <ul>
|
||||
* <li><code>WS_</code> Workspace path constant. Those are absolute paths,
|
||||
* from the project root.</li>
|
||||
* <li><code>OS_</code> OS path constant. These paths are different depending on the platform.</li>
|
||||
* <li><code>FN_</code> File name constant.</li>
|
||||
* <li><code>FD_</code> Folder name constant.</li>
|
||||
* <li><code>MARKER_</code> Resource Marker Ids constant.</li>
|
||||
* <li><code>EXT_</code> File extension constant. This does NOT include a dot.</li>
|
||||
* <li><code>DOT_</code> File extension constant. This start with a dot.</li>
|
||||
* <li><code>RE_</code> Regexp constant.</li>
|
||||
* <li><code>BUILD_</code> Build verbosity level constant. To use with
|
||||
* <code>AdtPlugin.printBuildToConsole()</code></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AdtConstants {
|
||||
/** Generic marker for ADT errors. */
|
||||
public final static String MARKER_ADT = AdtPlugin.PLUGIN_ID + ".adtProblem"; //$NON-NLS-1$
|
||||
|
||||
/** Build verbosity "Always". Those messages are always displayed. */
|
||||
public final static int BUILD_ALWAYS = 0;
|
||||
|
||||
/** Build verbosity level "Normal" */
|
||||
public final static int BUILD_NORMAL = 1;
|
||||
|
||||
/** Build verbosity level "Verbose". Those messages are only displayed in verbose mode */
|
||||
public final static int BUILD_VERBOSE = 2;
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
|
||||
package com.android.ide.eclipse.adt;
|
||||
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
public class Messages extends NLS {
|
||||
private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.messages"; //$NON-NLS-1$
|
||||
|
||||
public static String AdtPlugin_Android_SDK_Content_Loader;
|
||||
|
||||
public static String AdtPlugin_Android_SDK_Resource_Parser;
|
||||
|
||||
public static String AdtPlugin_Failed_To_Parse_s;
|
||||
|
||||
public static String AdtPlugin_Failed_To_Start_s;
|
||||
|
||||
public static String AdtPlugin_Parsing_Resources;
|
||||
|
||||
public static String Could_Not_Find;
|
||||
|
||||
public static String Could_Not_Find_Folder;
|
||||
|
||||
public static String Could_Not_Find_Folder_In_SDK;
|
||||
|
||||
public static String Dialog_Title_SDK_Location;
|
||||
|
||||
public static String Error_Check_Prefs;
|
||||
|
||||
public static String SDK_Not_Setup;
|
||||
|
||||
public static String VersionCheck_Plugin_Too_Old;
|
||||
|
||||
public static String VersionCheck_Plugin_Version_Failed;
|
||||
|
||||
public static String VersionCheck_SDK_Build_Too_Low;
|
||||
|
||||
public static String VersionCheck_SDK_Milestone_Too_Low;
|
||||
|
||||
public static String VersionCheck_Unable_To_Parse_Version_s;
|
||||
|
||||
static {
|
||||
// initialize resource bundle
|
||||
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
|
||||
}
|
||||
|
||||
private Messages() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.framework.Version;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Class handling the version check for the plugin vs. the SDK.<br>
|
||||
* The plugin must be able to support all version of the SDK.
|
||||
*
|
||||
* <p/>An SDK can require a new version of the plugin.
|
||||
* <p/>The SDK contains a file with the minimum version for the plugin. This file is inside the
|
||||
* <code>tools/lib</code> directory, and is called <code>plugin.prop</code>.<br>
|
||||
* Inside that text file, there is a line in the format "plugin.version=#.#.#". This is checked
|
||||
* against the current plugin version.<br>
|
||||
*
|
||||
*/
|
||||
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>.
|
||||
*/
|
||||
private final static Pattern sPluginVersionPattern = Pattern.compile(
|
||||
"^plugin.version=(\\d+)\\.(\\d+)\\.(\\d+).*$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Checks the plugin and the SDK have compatible versions.
|
||||
* @param osSdkPath The path to the SDK
|
||||
* @return true if compatible.
|
||||
*/
|
||||
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.
|
||||
}
|
||||
|
||||
// get the plugin property file, and grab the minimum plugin version required
|
||||
// to work with the sdk
|
||||
int minMajorVersion = -1;
|
||||
int minMinorVersion = -1;
|
||||
int minMicroVersion = -1;
|
||||
try {
|
||||
FileReader reader = new FileReader(osLibs + AndroidConstants.FN_PLUGIN_PROP);
|
||||
BufferedReader bReader = new BufferedReader(reader);
|
||||
String line;
|
||||
while ((line = bReader.readLine()) != null) {
|
||||
Matcher m = sPluginVersionPattern.matcher(line);
|
||||
if (m.matches()) {
|
||||
minMajorVersion = Integer.parseInt(m.group(1));
|
||||
minMinorVersion = Integer.parseInt(m.group(2));
|
||||
minMicroVersion = Integer.parseInt(m.group(3));
|
||||
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.
|
||||
}
|
||||
|
||||
// Failed to get the min plugin version number?
|
||||
if (minMajorVersion == -1 || minMinorVersion == -1 || minMicroVersion ==-1) {
|
||||
return errorHandler.handleWarning(Messages.VersionCheck_Plugin_Version_Failed);
|
||||
}
|
||||
|
||||
// test the plugin number
|
||||
String versionString = (String) plugin.getBundle().getHeaders().get(
|
||||
Constants.BUNDLE_VERSION);
|
||||
Version version = new Version(versionString);
|
||||
|
||||
boolean valid = true;
|
||||
if (version.getMajor() < minMajorVersion) {
|
||||
valid = false;
|
||||
} else if (version.getMajor() == minMajorVersion) {
|
||||
if (version.getMinor() < minMinorVersion) {
|
||||
valid = false;
|
||||
} else if (version.getMinor() == minMinorVersion) {
|
||||
if (version.getMicro() < minMicroVersion) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid == false) {
|
||||
return errorHandler.handleWarning(
|
||||
String.format(Messages.VersionCheck_Plugin_Too_Old,
|
||||
minMajorVersion, minMinorVersion, minMicroVersion, versionString));
|
||||
}
|
||||
|
||||
return true; // no error!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* 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.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.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.jarutils.DebugKeyProvider;
|
||||
import com.android.jarutils.JavaResourceFilter;
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
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 org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
public class ApkBuilder extends BaseBuilder {
|
||||
|
||||
public static final String ID = "com.android.ide.eclipse.adt.ApkBuilder"; //$NON-NLS-1$
|
||||
|
||||
private static final String PROPERTY_CONVERT_TO_DEX = "convertToDex"; //$NON-NLS-1$
|
||||
private static final String PROPERTY_PACKAGE_RESOURCES = "packageResources"; //$NON-NLS-1$
|
||||
private static final String PROPERTY_BUILD_APK = "buildApk"; //$NON-NLS-1$
|
||||
|
||||
private static final String DX_PREFIX = "Dx"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Dex conversion flag. This is set to true if one of the changed/added/removed
|
||||
* file is a .class file. Upon visiting all the delta resource, if this
|
||||
* flag is true, then we know we'll have to make the "classes.dex" file.
|
||||
*/
|
||||
private boolean mConvertToDex = false;
|
||||
|
||||
/**
|
||||
* Package resources flag. This is set to true if one of the changed/added/removed
|
||||
* file is a resource file. Upon visiting all the delta resource, if
|
||||
* this flag is true, then we know we'll have to repackage the resources.
|
||||
*/
|
||||
private boolean mPackageResources = false;
|
||||
|
||||
/**
|
||||
* Final package build flag.
|
||||
*/
|
||||
private boolean mBuildFinalPackage = false;
|
||||
|
||||
private PrintStream mOutStream = null;
|
||||
private PrintStream mErrStream = null;
|
||||
|
||||
/**
|
||||
* Basic Resource Delta Visitor class to check if a referenced project had a change in its
|
||||
* compiled java files.
|
||||
*/
|
||||
private static class ReferencedProjectDeltaVisitor implements IResourceDeltaVisitor {
|
||||
|
||||
private boolean mConvertToDex = false;
|
||||
private boolean mMakeFinalPackage;
|
||||
|
||||
private IPath mOutputFolder;
|
||||
private ArrayList<IPath> mSourceFolders;
|
||||
|
||||
private ReferencedProjectDeltaVisitor(IJavaProject javaProject) {
|
||||
try {
|
||||
mOutputFolder = javaProject.getOutputLocation();
|
||||
mSourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
|
||||
} catch (JavaModelException e) {
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean visit(IResourceDelta delta) throws CoreException {
|
||||
// no need to keep looking if we already know we need to convert
|
||||
// to dex and make the final package.
|
||||
if (mConvertToDex && mMakeFinalPackage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the resource and the path segments.
|
||||
IResource resource = delta.getResource();
|
||||
IPath resourceFullPath = resource.getFullPath();
|
||||
|
||||
if (mOutputFolder.isPrefixOf(resourceFullPath)) {
|
||||
int type = resource.getType();
|
||||
if (type == IResource.FILE) {
|
||||
String ext = resource.getFileExtension();
|
||||
if (AndroidConstants.EXT_CLASS.equals(ext)) {
|
||||
mConvertToDex = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
for (IPath sourceFullPath : mSourceFolders) {
|
||||
if (sourceFullPath.isPrefixOf(resourceFullPath)) {
|
||||
int type = resource.getType();
|
||||
if (type == IResource.FILE) {
|
||||
// check if the file is a valid file that would be
|
||||
// included during the final packaging.
|
||||
if (checkFileForPackaging((IFile)resource)) {
|
||||
mMakeFinalPackage = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if (type == IResource.FOLDER) {
|
||||
// if this is a folder, we check if this is a valid folder as well.
|
||||
// If this is a folder that needs to be ignored, we must return false,
|
||||
// so that we ignore its content.
|
||||
return checkFolderForPackaging((IFolder)resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if one of the .class file was modified.
|
||||
*/
|
||||
boolean needDexConvertion() {
|
||||
return mConvertToDex;
|
||||
}
|
||||
|
||||
boolean needMakeFinalPackage() {
|
||||
return mMakeFinalPackage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link IZipEntryFilter} to filter out everything that is not a standard java resources.
|
||||
* <p/>Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when
|
||||
* we only want the java resources from external jars.
|
||||
*/
|
||||
private final IZipEntryFilter mJavaResourcesFilter = new JavaResourceFilter();
|
||||
|
||||
public ApkBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
// build() returns a list of project from which this project depends for future compilation.
|
||||
@SuppressWarnings("unchecked") //$NON-NLS-1$
|
||||
@Override
|
||||
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
|
||||
throws CoreException {
|
||||
// get a project object
|
||||
IProject project = getProject();
|
||||
|
||||
// get the list of referenced projects.
|
||||
IProject[] referencedProjects = ProjectHelper.getReferencedProjects(project);
|
||||
IJavaProject[] referencedJavaProjects = getJavaProjects(referencedProjects);
|
||||
|
||||
// get the output folder, this method returns the path with a trailing
|
||||
// separator
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
IFolder outputFolder = BaseProjectHelper.getOutputFolder(project);
|
||||
|
||||
// now we need to get the classpath list
|
||||
ArrayList<IPath> sourceList = BaseProjectHelper.getSourceClasspaths(javaProject);
|
||||
|
||||
// First thing we do is go through the resource delta to not
|
||||
// lose it if we have to abort the build for any reason.
|
||||
if (kind == FULL_BUILD) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.Start_Full_Apk_Build);
|
||||
|
||||
mPackageResources = true;
|
||||
mConvertToDex = true;
|
||||
mBuildFinalPackage = true;
|
||||
} else {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.Start_Inc_Apk_Build);
|
||||
|
||||
// go through the resources and see if something changed.
|
||||
IResourceDelta delta = getDelta(project);
|
||||
if (delta == null) {
|
||||
mPackageResources = true;
|
||||
mConvertToDex = true;
|
||||
mBuildFinalPackage = true;
|
||||
} else {
|
||||
ApkDeltaVisitor dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
|
||||
delta.accept(dv);
|
||||
|
||||
// save the state
|
||||
mPackageResources |= dv.getPackageResources();
|
||||
mConvertToDex |= dv.getConvertToDex();
|
||||
mBuildFinalPackage |= dv.getMakeFinalPackage();
|
||||
|
||||
if (dv.mXmlError) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.Xml_Error);
|
||||
|
||||
// if there was some XML errors, we just return w/o doing
|
||||
// anything since we've put some markers in the files anyway
|
||||
return referencedProjects;
|
||||
}
|
||||
}
|
||||
|
||||
// also go through the delta for all the referenced projects, until we are forced to
|
||||
// compile anyway
|
||||
for (int i = 0 ; i < referencedJavaProjects.length &&
|
||||
(mBuildFinalPackage == false || mConvertToDex == false); i++) {
|
||||
IJavaProject referencedJavaProject = referencedJavaProjects[i];
|
||||
delta = getDelta(referencedJavaProject.getProject());
|
||||
if (delta != null) {
|
||||
ReferencedProjectDeltaVisitor dv = new ReferencedProjectDeltaVisitor(
|
||||
referencedJavaProject);
|
||||
delta.accept(dv);
|
||||
|
||||
// save the state
|
||||
mConvertToDex |= dv.needDexConvertion();
|
||||
mBuildFinalPackage |= dv.needMakeFinalPackage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do some extra check, in case the output files are not present. This
|
||||
// will force to recreate them.
|
||||
IResource tmp = null;
|
||||
|
||||
if (mPackageResources == false && outputFolder != null) {
|
||||
tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
|
||||
if (tmp == null || tmp.exists() == false) {
|
||||
mPackageResources = true;
|
||||
mBuildFinalPackage = true;
|
||||
}
|
||||
}
|
||||
if (mConvertToDex == false && outputFolder != null) {
|
||||
tmp = outputFolder.findMember(AndroidConstants.FN_CLASSES_DEX);
|
||||
if (tmp == null || tmp.exists() == false) {
|
||||
mConvertToDex = true;
|
||||
mBuildFinalPackage = true;
|
||||
}
|
||||
}
|
||||
|
||||
// also check the final file!
|
||||
String finalPackageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
|
||||
if (mBuildFinalPackage == false && outputFolder != null) {
|
||||
tmp = outputFolder.findMember(finalPackageName);
|
||||
if (tmp == null || (tmp instanceof IFile &&
|
||||
tmp.exists() == false)) {
|
||||
String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
|
||||
mBuildFinalPackage = true;
|
||||
}
|
||||
}
|
||||
|
||||
// store the build status in the persistent storage
|
||||
saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX , mConvertToDex);
|
||||
saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources);
|
||||
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
|
||||
|
||||
// Now check the compiler compliance level, not displaying the error
|
||||
// message since this is not the first builder.
|
||||
if (ProjectHelper.checkCompilerCompliance(getProject())
|
||||
!= ProjectHelper.COMPILER_COMPLIANCE_OK) {
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// now check if the project has problem marker already
|
||||
if (ProjectHelper.hasError(project, true)) {
|
||||
// we found a marker with error severity: we abort the build.
|
||||
// Since this is going to happen every time we save a file while
|
||||
// errors are remaining, we do not force the display of the console, which
|
||||
// would, in most cases, show on top of the Problem view (which is more
|
||||
// important in that case).
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.Project_Has_Errors);
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
if (outputFolder == null) {
|
||||
// mark project and exit
|
||||
markProject(AdtConstants.MARKER_ADT, Messages.Failed_To_Get_Output,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// first thing we do is check that the SDK directory has been setup.
|
||||
String osSdkFolder = AdtPlugin.getOsSdkFolder();
|
||||
|
||||
if (osSdkFolder.length() == 0) {
|
||||
// this has already been checked in the precompiler. Therefore,
|
||||
// while we do have to cancel the build, we don't have to return
|
||||
// any error or throw anything.
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// at this point we know if we need to recreate the temporary apk
|
||||
// or the dex file, but we don't know if we simply need to recreate them
|
||||
// because they are missing
|
||||
|
||||
// refresh the output directory first
|
||||
IContainer ic = outputFolder.getParent();
|
||||
if (ic != null) {
|
||||
ic.refreshLocal(IResource.DEPTH_ONE, monitor);
|
||||
}
|
||||
|
||||
// we need to test all three, as we may need to make the final package
|
||||
// but not the intermediary ones.
|
||||
if (mPackageResources || mConvertToDex || mBuildFinalPackage) {
|
||||
IPath binLocation = outputFolder.getLocation();
|
||||
if (binLocation == null) {
|
||||
markProject(AdtConstants.MARKER_ADT, Messages.Output_Missing,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
return referencedProjects;
|
||||
}
|
||||
String osBinPath = binLocation.toOSString();
|
||||
|
||||
// Remove the old .apk.
|
||||
// This make sure that if the apk is corrupted, then dx (which would attempt
|
||||
// to open it), will not fail.
|
||||
String osFinalPackagePath = osBinPath + File.separator + finalPackageName;
|
||||
File finalPackage = new File(osFinalPackagePath);
|
||||
|
||||
// if delete failed, this is not really a problem, as the final package generation
|
||||
// handle already present .apk, and if that one failed as well, the user will be
|
||||
// notified.
|
||||
finalPackage.delete();
|
||||
|
||||
// first we check if we need to package the resources.
|
||||
if (mPackageResources) {
|
||||
// need to figure out some path before we can execute aapt;
|
||||
|
||||
// resource to the AndroidManifest.xml file
|
||||
IResource manifestResource = project .findMember(
|
||||
AndroidConstants.WS_SEP + AndroidConstants.FN_ANDROID_MANIFEST);
|
||||
|
||||
if (manifestResource == null
|
||||
|| manifestResource.exists() == false) {
|
||||
// mark project and exit
|
||||
String msg = String.format(Messages.s_File_Missing,
|
||||
AndroidConstants.FN_ANDROID_MANIFEST);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// get the resource folder
|
||||
IFolder resFolder = project.getFolder(
|
||||
AndroidConstants.WS_RESOURCES);
|
||||
|
||||
// and the assets folder
|
||||
IFolder assetsFolder = project.getFolder(
|
||||
AndroidConstants.WS_ASSETS);
|
||||
|
||||
// we need to make sure this one exists.
|
||||
if (assetsFolder.exists() == false) {
|
||||
assetsFolder = null;
|
||||
}
|
||||
|
||||
IPath resLocation = resFolder.getLocation();
|
||||
IPath manifestLocation = manifestResource.getLocation();
|
||||
|
||||
if (resLocation != null && manifestLocation != null) {
|
||||
String osResPath = resLocation.toOSString();
|
||||
String osManifestPath = manifestLocation.toOSString();
|
||||
|
||||
String osAssetsPath = null;
|
||||
if (assetsFolder != null) {
|
||||
osAssetsPath = assetsFolder.getLocation().toOSString();
|
||||
}
|
||||
|
||||
if (executeAapt(project, osManifestPath, osResPath,
|
||||
osAssetsPath, osBinPath + File.separator +
|
||||
AndroidConstants.FN_RESOURCES_AP_) == false) {
|
||||
// aapt failed. Whatever files that needed to be marked
|
||||
// have already been marked. We just return.
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// build has been done. reset the state of the builder
|
||||
mPackageResources = false;
|
||||
|
||||
// and store it
|
||||
saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources);
|
||||
}
|
||||
}
|
||||
|
||||
// then we check if we need to package the .class into classes.dex
|
||||
if (mConvertToDex) {
|
||||
if (executeDx(javaProject, osBinPath, osBinPath + File.separator +
|
||||
AndroidConstants.FN_CLASSES_DEX, referencedJavaProjects) == false) {
|
||||
// dx failed, we return
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
// build has been done. reset the state of the builder
|
||||
mConvertToDex = false;
|
||||
|
||||
// and store it
|
||||
saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX, mConvertToDex);
|
||||
}
|
||||
|
||||
// now we need to make the final package from the intermediary apk
|
||||
// and classes.dex
|
||||
|
||||
if (finalPackage(osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_,
|
||||
osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX,
|
||||
osFinalPackagePath, javaProject, referencedJavaProjects) == false) {
|
||||
return referencedProjects;
|
||||
} else {
|
||||
// get the resource to bin
|
||||
outputFolder.refreshLocal(IResource.DEPTH_ONE, monitor);
|
||||
|
||||
// build has been done. reset the state of the builder
|
||||
mBuildFinalPackage = false;
|
||||
|
||||
// and store it
|
||||
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
|
||||
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
"Build Success!");
|
||||
}
|
||||
}
|
||||
return referencedProjects;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void startupOnInitialize() {
|
||||
super.startupOnInitialize();
|
||||
|
||||
// load the build status. We pass true as the default value to
|
||||
// force a recompile in case the property was not found
|
||||
mConvertToDex = loadProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX , true);
|
||||
mPackageResources = loadProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, true);
|
||||
mBuildFinalPackage = loadProjectBooleanProperty(PROPERTY_BUILD_APK, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes aapt. If any error happen, files or the project will be marked.
|
||||
* @param project The Project
|
||||
* @param osManifestPath The path to the manifest file
|
||||
* @param osResPath The path to the res folder
|
||||
* @param osAssetsPath The path to the assets folder. This can be null.
|
||||
* @param osOutFilePath The path to the temporary resource file to create.
|
||||
* @return true if success, false otherwise.
|
||||
*/
|
||||
private boolean executeAapt(IProject project, String osManifestPath,
|
||||
String osResPath, String osAssetsPath, String osOutFilePath) {
|
||||
|
||||
// Create the command line.
|
||||
ArrayList<String> commandArray = new ArrayList<String>();
|
||||
commandArray.add(AdtPlugin.getOsAbsoluteAapt());
|
||||
commandArray.add("package"); //$NON-NLS-1$
|
||||
commandArray.add("-f");//$NON-NLS-1$
|
||||
if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) {
|
||||
commandArray.add("-v"); //$NON-NLS-1$
|
||||
}
|
||||
commandArray.add("-M"); //$NON-NLS-1$
|
||||
commandArray.add(osManifestPath);
|
||||
commandArray.add("-S"); //$NON-NLS-1$
|
||||
commandArray.add(osResPath);
|
||||
if (osAssetsPath != null) {
|
||||
commandArray.add("-A"); //$NON-NLS-1$
|
||||
commandArray.add(osAssetsPath);
|
||||
}
|
||||
commandArray.add("-I"); //$NON-NLS-1$
|
||||
commandArray.add(AdtPlugin.getOsAbsoluteFramework());
|
||||
commandArray.add("-F"); //$NON-NLS-1$
|
||||
commandArray.add(osOutFilePath);
|
||||
|
||||
String command[] = commandArray.toArray(
|
||||
new String[commandArray.size()]);
|
||||
|
||||
if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String c : command) {
|
||||
sb.append(c);
|
||||
sb.append(' ');
|
||||
}
|
||||
AdtPlugin.printToConsole(project, sb.toString());
|
||||
}
|
||||
|
||||
// launch
|
||||
int execError = 1;
|
||||
try {
|
||||
// launch the command line process
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
// list to store each line of stderr
|
||||
ArrayList<String> results = new ArrayList<String>();
|
||||
|
||||
// get the output and return code from the process
|
||||
execError = grabProcessOutput(process, results);
|
||||
|
||||
// attempt to parse the error output
|
||||
boolean parsingError = parseAaptOutput(results, project);
|
||||
|
||||
// if we couldn't parse the output we display it in the console.
|
||||
if (parsingError) {
|
||||
if (execError != 0) {
|
||||
AdtPlugin.printErrorToConsole(project, results.toArray());
|
||||
} else {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, project,
|
||||
results.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
// We need to abort if the exec failed.
|
||||
if (execError != 0) {
|
||||
// if the exec failed, and we couldn't parse the error output (and therefore
|
||||
// not all files that should have been marked, were marked), we put a generic
|
||||
// marker on the project and abort.
|
||||
if (parsingError) {
|
||||
markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
}
|
||||
|
||||
// abort if exec failed.
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
} catch (InterruptedException e) {
|
||||
String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Dx tool for dalvik code conversion.
|
||||
* @param javaProject The java project
|
||||
* @param osBinPath the path to the output folder of the project
|
||||
* @param osOutFilePath the path of the dex file to create.
|
||||
* @param referencedJavaProjects the list of referenced projects for this project.
|
||||
* @return
|
||||
* @throws CoreException
|
||||
*/
|
||||
private boolean executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath,
|
||||
IJavaProject[] referencedJavaProjects) throws CoreException {
|
||||
// get the dex wrapper
|
||||
DexWrapper wrapper = DexWrapper.getWrapper();
|
||||
|
||||
if (wrapper == null) {
|
||||
if (DexWrapper.getStatus() == DexWrapper.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;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure dx use the proper output streams.
|
||||
// first make sure we actually have the streams available.
|
||||
if (mOutStream == null) {
|
||||
IProject project = getProject();
|
||||
mOutStream = AdtPlugin.getOutPrintStream(project, DX_PREFIX);
|
||||
mErrStream = AdtPlugin.getErrPrintStream(project, DX_PREFIX);
|
||||
}
|
||||
|
||||
try {
|
||||
// get the list of libraries to include with the source code
|
||||
String[] libraries = getExternalJars();
|
||||
|
||||
// get the list of referenced projects output to add
|
||||
String[] projectOutputs = getProjectOutputs(referencedJavaProjects);
|
||||
|
||||
String[] fileNames = new String[1 + projectOutputs.length + libraries.length];
|
||||
|
||||
// first this project output
|
||||
fileNames[0] = osBinPath;
|
||||
|
||||
// then other project output
|
||||
System.arraycopy(projectOutputs, 0, fileNames, 1, projectOutputs.length);
|
||||
|
||||
// then external jars.
|
||||
System.arraycopy(libraries, 0, fileNames, 1 + projectOutputs.length, libraries.length);
|
||||
|
||||
int res = wrapper.run(osOutFilePath, fileNames,
|
||||
AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE,
|
||||
mOutStream, mErrStream);
|
||||
|
||||
if (res != 0) {
|
||||
// output error message and marker the project.
|
||||
String message = String.format(Messages.Dalvik_Error_d,
|
||||
res);
|
||||
AdtPlugin.printErrorToConsole(getProject(), message);
|
||||
markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
String message = String.format(Messages.Dalvik_Error_s,
|
||||
ex.getMessage());
|
||||
AdtPlugin.printErrorToConsole(getProject(), message);
|
||||
markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR);
|
||||
if ((ex instanceof NoClassDefFoundError)
|
||||
|| (ex instanceof NoSuchMethodError)) {
|
||||
AdtPlugin.printErrorToConsole(getProject(), Messages.Incompatible_VM_Warning,
|
||||
Messages.Requires_1_5_Error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the final package. Package the dex files, the temporary resource file into the final
|
||||
* package file.
|
||||
* @param intermediateApk The path to the temporary resource file.
|
||||
* @param dex The path to the dex file.
|
||||
* @param output The path to the final package file to create.
|
||||
* @param javaProject
|
||||
* @param referencedJavaProjects
|
||||
* @return true if success, false otherwise.
|
||||
*/
|
||||
private boolean finalPackage(String intermediateApk, String dex, String output,
|
||||
final IJavaProject javaProject, IJavaProject[] referencedJavaProjects) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
|
||||
String osKeyPath = store.getString(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE);
|
||||
if (osKeyPath == null || new File(osKeyPath).exists() == false) {
|
||||
osKeyPath = DebugKeyProvider.getDefaultKeyStoreOsPath();
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
Messages.ApkBuilder_Using_Default_Key);
|
||||
} else {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
String.format(Messages.ApkBuilder_Using_s_To_Sign, osKeyPath));
|
||||
}
|
||||
|
||||
// TODO: get the store type from somewhere else.
|
||||
DebugKeyProvider provider = new DebugKeyProvider(null /* storeType */,
|
||||
new IKeyGenOutput() {
|
||||
public void err(String message) {
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(),
|
||||
Messages.ApkBuilder_Signing_Key_Creation_s + message);
|
||||
}
|
||||
|
||||
public void out(String message) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
|
||||
javaProject.getProject(),
|
||||
Messages.ApkBuilder_Signing_Key_Creation_s + message);
|
||||
}
|
||||
});
|
||||
PrivateKey key = provider.getDebugKey();
|
||||
X509Certificate certificate = (X509Certificate)provider.getCertificate();
|
||||
|
||||
if (key == null) {
|
||||
String msg = String.format(Messages.Final_Archive_Error_s,
|
||||
Messages.ApkBuilder_Unable_To_Gey_Key);
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// compare the certificate expiration date
|
||||
if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) {
|
||||
// TODO, regenerate a new one.
|
||||
String msg = String.format(Messages.Final_Archive_Error_s,
|
||||
String.format(Messages.ApkBuilder_Certificate_Expired_on_s,
|
||||
DateFormat.getInstance().format(certificate.getNotAfter())));
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the jar builder.
|
||||
fos = new FileOutputStream(output);
|
||||
SignedJarBuilder builder = new SignedJarBuilder(fos, key, certificate);
|
||||
|
||||
// add the intermediate file containing the compiled resources.
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
String.format(Messages.ApkBuilder_Packaging_s, intermediateApk));
|
||||
FileInputStream fis = new FileInputStream(intermediateApk);
|
||||
try {
|
||||
builder.writeZip(fis, null /* filter */);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
// Now we add the new file to the zip archive for the classes.dex file.
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
String.format(Messages.ApkBuilder_Packaging_s, AndroidConstants.FN_CLASSES_DEX));
|
||||
File entryFile = new File(dex);
|
||||
builder.writeFile(entryFile, AndroidConstants.FN_CLASSES_DEX);
|
||||
|
||||
// Now we write the standard resources from the project and the referenced projects.
|
||||
writeStandardResources(builder, javaProject, referencedJavaProjects);
|
||||
|
||||
// Now we write the standard resources from the external libraries
|
||||
for (String libraryOsPath : getExternalJars()) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
String.format(Messages.ApkBuilder_Packaging_s, libraryOsPath));
|
||||
try {
|
||||
fis = new FileInputStream(libraryOsPath);
|
||||
builder.writeZip(fis, mJavaResourcesFilter);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
} catch (IOException e1) {
|
||||
// mark project and return
|
||||
String msg = String.format(Messages.Final_Archive_Error_s, e1.getMessage());
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
return false;
|
||||
} catch (KeytoolException e) {
|
||||
String eMessage = e.getMessage();
|
||||
|
||||
// mark the project with the standard message
|
||||
String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
|
||||
// output more info in the console
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(),
|
||||
msg,
|
||||
String.format(Messages.ApkBuilder_JAVA_HOME_is_s, e.getJavaHome()),
|
||||
Messages.ApkBuilder_Update_or_Execute_manually_s,
|
||||
e.getCommandLine());
|
||||
} catch (AndroidLocationException e) {
|
||||
String eMessage = e.getMessage();
|
||||
|
||||
// mark the project with the standard message
|
||||
String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
|
||||
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
|
||||
|
||||
// and also output it in the console
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(), msg);
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
// pass.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the standard resources of a project and its referenced projects
|
||||
* into a {@link SignedJarBuilder}.
|
||||
* Standard resources are non java/aidl files placed in the java package folders.
|
||||
* @param jarBuilder the {@link SignedJarBuilder}.
|
||||
* @param javaProject the javaProject object.
|
||||
* @param referencedJavaProjects the java projects that this project references.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeStandardResources(SignedJarBuilder jarBuilder, IJavaProject javaProject,
|
||||
IJavaProject[] referencedJavaProjects) throws IOException {
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
IWorkspaceRoot wsRoot = ws.getRoot();
|
||||
|
||||
// create a list of path already put into the archive, in order to detect conflict
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
writeStandardProjectResources(jarBuilder, javaProject, wsRoot, list);
|
||||
|
||||
for (IJavaProject referencedJavaProject : referencedJavaProjects) {
|
||||
writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the standard resources of a {@link IJavaProject} into a {@link SignedJarBuilder}.
|
||||
* Standard resources are non java/aidl files placed in the java package folders.
|
||||
* @param jarBuilder the {@link SignedJarBuilder}.
|
||||
* @param javaProject the javaProject object.
|
||||
* @param wsRoot the {@link IWorkspaceRoot}.
|
||||
* @param list a list of files already added to the archive, to detect conflicts.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeStandardProjectResources(SignedJarBuilder jarBuilder,
|
||||
IJavaProject javaProject, IWorkspaceRoot wsRoot, ArrayList<String> list)
|
||||
throws IOException {
|
||||
// get the source pathes
|
||||
ArrayList<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
|
||||
|
||||
// loop on them and then recursively go through the content looking for matching files.
|
||||
for (IPath sourcePath : sourceFolders) {
|
||||
IResource sourceResource = wsRoot.findMember(sourcePath);
|
||||
if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) {
|
||||
writeStandardSourceFolderResources(jarBuilder, sourcePath, (IFolder)sourceResource,
|
||||
list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively writes the standard resources of a source folder into a {@link SignedJarBuilder}.
|
||||
* Standard resources are non java/aidl files placed in the java package folders.
|
||||
* @param jarBuilder the {@link SignedJarBuilder}.
|
||||
* @param sourceFolder the {@link IPath} of the source folder.
|
||||
* @param currentFolder The current folder we're recursively processing.
|
||||
* @param list a list of files already added to the archive, to detect conflicts.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeStandardSourceFolderResources(SignedJarBuilder jarBuilder, IPath sourceFolder,
|
||||
IFolder currentFolder, ArrayList<String> list) throws IOException {
|
||||
try {
|
||||
IResource[] members = currentFolder.members();
|
||||
|
||||
for (IResource member : members) {
|
||||
int type = member.getType();
|
||||
if (type == IResource.FILE && member.exists()) {
|
||||
if (checkFileForPackaging((IFile)member)) {
|
||||
// this files must be added to the archive.
|
||||
IPath fullPath = member.getFullPath();
|
||||
|
||||
// We need to create its path inside the archive.
|
||||
// This path is relative to the source folder.
|
||||
IPath relativePath = fullPath.removeFirstSegments(
|
||||
sourceFolder.segmentCount());
|
||||
String zipPath = relativePath.toString();
|
||||
|
||||
// lets check it's not already in the list of path added to the archive
|
||||
if (list.indexOf(zipPath) != -1) {
|
||||
AdtPlugin.printErrorToConsole(getProject(),
|
||||
String.format(
|
||||
Messages.ApkBuilder_s_Conflict_with_file_s,
|
||||
fullPath, zipPath));
|
||||
} else {
|
||||
// get the File object
|
||||
File entryFile = member.getLocation().toFile();
|
||||
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
|
||||
String.format(Messages.ApkBuilder_Packaging_s_into_s, fullPath, zipPath));
|
||||
|
||||
// write it in the zip archive
|
||||
jarBuilder.writeFile(entryFile, zipPath);
|
||||
|
||||
// and add it to the list of entries
|
||||
list.add(zipPath);
|
||||
}
|
||||
}
|
||||
} else if (type == IResource.FOLDER) {
|
||||
if (checkFolderForPackaging((IFolder)member)) {
|
||||
writeStandardSourceFolderResources(jarBuilder, sourceFolder,
|
||||
(IFolder)member, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// if we can't get the members of the folder, we just don't do anything.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of the output folders for the specified {@link IJavaProject} objects.
|
||||
* @param referencedJavaProjects the java projects.
|
||||
* @return an array, always. Can be empty.
|
||||
* @throws CoreException
|
||||
*/
|
||||
private String[] getProjectOutputs(IJavaProject[] referencedJavaProjects) throws CoreException {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
IWorkspaceRoot wsRoot = ws.getRoot();
|
||||
|
||||
for (IJavaProject javaProject : referencedJavaProjects) {
|
||||
// get the output folder
|
||||
IPath path = null;
|
||||
try {
|
||||
path = javaProject.getOutputLocation();
|
||||
} catch (JavaModelException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IResource outputResource = wsRoot.findMember(path);
|
||||
if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
|
||||
String outputOsPath = outputResource.getLocation().toOSString();
|
||||
|
||||
list.add(outputOsPath);
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@link IJavaProject} matching the provided {@link IProject} objects.
|
||||
* @param projects the IProject objects.
|
||||
* @return an array, always. Can be empty.
|
||||
* @throws CoreException
|
||||
*/
|
||||
private IJavaProject[] getJavaProjects(IProject[] projects) throws CoreException {
|
||||
ArrayList<IJavaProject> list = new ArrayList<IJavaProject>();
|
||||
|
||||
for (IProject p : projects) {
|
||||
if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
|
||||
|
||||
list.add(JavaCore.create(p));
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new IJavaProject[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a {@link IFile} to make sure it should be packaged as standard resources.
|
||||
* @param file the IFile representing the file.
|
||||
* @return true if the file should be packaged as standard java resources.
|
||||
*/
|
||||
static boolean checkFileForPackaging(IFile file) {
|
||||
String name = file.getName();
|
||||
|
||||
String ext = file.getFileExtension();
|
||||
return JavaResourceFilter.checkFileForPackaging(name, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an {@link IFolder} and its content is valid for packaging into the .apk as
|
||||
* standard Java resource.
|
||||
* @param folder the {@link IFolder} to check.
|
||||
*/
|
||||
static boolean checkFolderForPackaging(IFolder folder) {
|
||||
String name = folder.getName();
|
||||
return JavaResourceFilter.checkFolderForPackaging(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Delta resource visitor looking for changes that will trigger a new packaging of an Android
|
||||
* application.
|
||||
* <p/>
|
||||
* This looks for the following changes:
|
||||
* <ul>
|
||||
* <li>Any change to the AndroidManifest.xml file</li>
|
||||
* <li>Any change inside the assets/ folder</li>
|
||||
* <li>Any file change inside the res/ folder</li>
|
||||
* <li>Any .class file change inside the output folder</li>
|
||||
* <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>
|
||||
* </ul>
|
||||
*/
|
||||
public class ApkDeltaVisitor extends BaseDeltaVisitor
|
||||
implements IResourceDeltaVisitor {
|
||||
|
||||
/**
|
||||
* compile flag. This is set to true if one of the changed/added/removed
|
||||
* file is a .class file. Upon visiting all the delta resources, if this
|
||||
* flag is true, then we know we'll have to make the "classes.dex" file.
|
||||
*/
|
||||
private boolean mConvertToDex = false;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* this flag is true, then we know we'll have to make the intermediate
|
||||
* apk file.
|
||||
*/
|
||||
private boolean mPackageResources = false;
|
||||
|
||||
/**
|
||||
* Final package flag. This is set to true if one of the changed/added/removed
|
||||
* file is a non java file (or aidl) in the resource folder. Upon visiting all the
|
||||
* delta resources, if this flag is true, then we know we'll have to make the final
|
||||
* package.
|
||||
*/
|
||||
private boolean mMakeFinalPackage = false;
|
||||
|
||||
/** List of source folders. */
|
||||
private ArrayList<IPath> mSourceFolders;
|
||||
|
||||
private IPath mOutputPath;
|
||||
|
||||
private IPath mAssetPath;
|
||||
|
||||
private IPath mResPath;
|
||||
|
||||
/**
|
||||
* Builds the object with a specified output folder.
|
||||
* @param builder the xml builder using this object to visit the
|
||||
* resource delta.
|
||||
* @param sourceFolders the list of source folders for the project, relative to the workspace.
|
||||
* @param outputfolder the output folder of the project.
|
||||
*/
|
||||
public ApkDeltaVisitor(BaseBuilder builder, ArrayList<IPath> sourceFolders,
|
||||
IFolder outputfolder) {
|
||||
super(builder);
|
||||
mSourceFolders = sourceFolders;
|
||||
|
||||
if (outputfolder != null) {
|
||||
mOutputPath = outputfolder.getFullPath();
|
||||
}
|
||||
|
||||
IResource assetFolder = builder.getProject().findMember(AndroidConstants.FD_ASSETS);
|
||||
if (assetFolder != null) {
|
||||
mAssetPath = assetFolder.getFullPath();
|
||||
}
|
||||
|
||||
IResource resFolder = builder.getProject().findMember(AndroidConstants.FD_RESOURCES);
|
||||
if (resFolder != null) {
|
||||
mResPath = resFolder.getFullPath();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getConvertToDex() {
|
||||
return mConvertToDex;
|
||||
}
|
||||
|
||||
public boolean getPackageResources() {
|
||||
return mPackageResources;
|
||||
}
|
||||
|
||||
public boolean getMakeFinalPackage() {
|
||||
return mMakeFinalPackage;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.core.resources.IResourceDeltaVisitor
|
||||
* #visit(org.eclipse.core.resources.IResourceDelta)
|
||||
*/
|
||||
public boolean visit(IResourceDelta delta) throws CoreException {
|
||||
// if all flags are true, we can stop going through the resource delta.
|
||||
if (mConvertToDex && mPackageResources && mMakeFinalPackage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we are only going to look for changes in res/, src/ and in
|
||||
// AndroidManifest.xml since the delta visitor goes through the main
|
||||
// folder before its childre we can check when the path segment
|
||||
// count is 2 (format will be /$Project/folder) and make sure we are
|
||||
// processing res/, src/ or AndroidManifest.xml
|
||||
IResource resource = delta.getResource();
|
||||
IPath path = resource.getFullPath();
|
||||
String[] pathSegments = path.segments();
|
||||
int type = resource.getType();
|
||||
|
||||
// since the delta visitor also visits the root we return true if
|
||||
// segments.length = 1
|
||||
if (pathSegments.length == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check the manifest.
|
||||
if (pathSegments.length == 2 &&
|
||||
AndroidConstants.FN_ANDROID_MANIFEST.equalsIgnoreCase(pathSegments[1])) {
|
||||
// if the manifest changed we have to repackage the
|
||||
// resources.
|
||||
mPackageResources = true;
|
||||
mMakeFinalPackage = true;
|
||||
|
||||
// we don't want to go to the children, not like they are
|
||||
// any for this resource anyway.
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the other folders.
|
||||
if (mOutputPath != null && mOutputPath.isPrefixOf(path)) {
|
||||
if (type == IResource.FILE) {
|
||||
// just check this is a .class file. Any modification will
|
||||
// trigger a change in the classes.dex file
|
||||
String ext = resource.getFileExtension();
|
||||
if (AndroidConstants.EXT_CLASS.equalsIgnoreCase(ext)) {
|
||||
mConvertToDex = true;
|
||||
mMakeFinalPackage = true;
|
||||
|
||||
// no need to check the children, as we are in a package
|
||||
// and there can only be subpackage children containing
|
||||
// only .class files
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for a few files directly in the output folder and force
|
||||
// rebuild if they have been deleted.
|
||||
if (delta.getKind() == IResourceDelta.REMOVED) {
|
||||
IPath parentPath = path.removeLastSegments(1);
|
||||
if (mOutputPath.equals(parentPath)) {
|
||||
String resourceName = resource.getName();
|
||||
if (resourceName.equalsIgnoreCase(AndroidConstants.FN_CLASSES_DEX)) {
|
||||
mConvertToDex = true;
|
||||
mMakeFinalPackage = true;
|
||||
} else if (resourceName.equalsIgnoreCase(
|
||||
AndroidConstants.FN_RESOURCES_AP_)) {
|
||||
mPackageResources = true;
|
||||
mMakeFinalPackage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if this is a folder, we only go visit it if we don't already know
|
||||
// that we need to convert to dex already.
|
||||
return mConvertToDex == false;
|
||||
} else if (mResPath != null && mResPath.isPrefixOf(path)) {
|
||||
// in the res folder we are looking for any file modification
|
||||
// (we don't care about folder being added/removed, only content
|
||||
// is important)
|
||||
if (type == IResource.FILE) {
|
||||
mPackageResources = true;
|
||||
mMakeFinalPackage = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// for folders, return true only if we don't already know we have to
|
||||
// package the resources.
|
||||
return mPackageResources == false;
|
||||
} else if (mAssetPath != null && mAssetPath.isPrefixOf(path)) {
|
||||
// this is the assets folder that was modified.
|
||||
// we don't care what content was changed. All we care
|
||||
// about is that something changed inside. No need to visit
|
||||
// the children even.
|
||||
mPackageResources = true;
|
||||
mMakeFinalPackage = true;
|
||||
return 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
|
||||
// the final package.
|
||||
// This could be a source folder or a folder leading to a source folder.
|
||||
// However we only check this if we don't already know that we need to build the
|
||||
// package anyway
|
||||
if (mMakeFinalPackage == false) {
|
||||
for (IPath sourcePath : mSourceFolders) {
|
||||
if (sourcePath.isPrefixOf(path)) {
|
||||
// In the source folders, we are looking for any kind of
|
||||
// modification related to file that are not java files.
|
||||
// Also excluded are aidl files, and package.html files
|
||||
if (type == IResource.FOLDER) {
|
||||
// always visit the subfolders, unless the folder is not to be included
|
||||
return ApkBuilder.checkFolderForPackaging((IFolder)resource);
|
||||
} else if (type == IResource.FILE) {
|
||||
if (ApkBuilder.checkFileForPackaging((IFile)resource)) {
|
||||
mMakeFinalPackage = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the folder is not inside one of the folders we are interested in (res, assets, output,
|
||||
// source folders), it could be a folder leading to them, so we return true.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,851 @@
|
||||
/*
|
||||
* 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.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.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.XmlErrorHandler;
|
||||
import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* Base builder for XML files. This class allows for basic XML parsing with
|
||||
* error checking and marking the files for errors/warnings.
|
||||
*/
|
||||
abstract class BaseBuilder extends IncrementalProjectBuilder {
|
||||
|
||||
// TODO: rename the pattern to something that makes sense + javadoc comments.
|
||||
|
||||
/**
|
||||
* Single line aapt warning for skipping files.<br>
|
||||
* " (skipping hidden file '<file path>'"
|
||||
*/
|
||||
private final static Pattern sPattern0Line1 = Pattern.compile(
|
||||
"^\\s+\\(skipping hidden file\\s'(.*)'\\)$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* First line of dual line aapt error.<br>
|
||||
* "ERROR at line <line>: <error>"<br>
|
||||
* " (Occured while parsing <path>)"
|
||||
*/
|
||||
private final static Pattern sPattern1Line1 = Pattern.compile(
|
||||
"^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$
|
||||
/**
|
||||
* Second line of dual line aapt error.<br>
|
||||
* "ERROR at line <line>: <error>"<br>
|
||||
* " (Occured while parsing <path>)"<br>
|
||||
* @see sPattern1Line1
|
||||
*/
|
||||
private final static Pattern sPattern1Line2 = Pattern.compile(
|
||||
"^\\s+\\(Occurred while parsing\\s+(.*)\\)$"); //$NON-NLS-1$
|
||||
/**
|
||||
* First line of dual line aapt error.<br>
|
||||
* "ERROR: <error>"<br>
|
||||
* "Defined at file <path> line <line>"
|
||||
*/
|
||||
private final static Pattern sPattern2Line1 = Pattern.compile(
|
||||
"^ERROR:\\s+(.+)$"); //$NON-NLS-1$
|
||||
/**
|
||||
* Second line of dual line aapt error.<br>
|
||||
* "ERROR: <error>"<br>
|
||||
* "Defined at file <path> line <line>"<br>
|
||||
* @see sPattern2Line1
|
||||
*/
|
||||
private final static Pattern sPattern2Line2 = Pattern.compile(
|
||||
"Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$
|
||||
/**
|
||||
* Single line aapt error<br>
|
||||
* "<path> line <line>: <error>"
|
||||
*/
|
||||
private final static Pattern sPattern3Line1 = Pattern.compile(
|
||||
"^(.+)\\sline\\s(\\d+):\\s(.+)$"); //$NON-NLS-1$
|
||||
/**
|
||||
* First line of dual line aapt error.<br>
|
||||
* "ERROR parsing XML file <path>"<br>
|
||||
* "<error> at line <line>"
|
||||
*/
|
||||
private final static Pattern sPattern4Line1 = Pattern.compile(
|
||||
"^Error\\s+parsing\\s+XML\\s+file\\s(.+)$"); //$NON-NLS-1$
|
||||
/**
|
||||
* Second line of dual line aapt error.<br>
|
||||
* "ERROR parsing XML file <path>"<br>
|
||||
* "<error> at line <line>"<br>
|
||||
* @see sPattern4Line1
|
||||
*/
|
||||
private final static Pattern sPattern4Line2 = Pattern.compile(
|
||||
"^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Single line aapt warning<br>
|
||||
* "<path>:<line>: <error>"
|
||||
*/
|
||||
private final static Pattern sPattern5Line1 = Pattern.compile(
|
||||
"^(.+?):(\\d+):\\s+WARNING:(.+)$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Single line aapt error<br>
|
||||
* "<path>:<line>: <error>"
|
||||
*/
|
||||
private final static Pattern sPattern6Line1 = Pattern.compile(
|
||||
"^(.+?):(\\d+):\\s+(.+)$"); //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* 4 line aapt error<br>
|
||||
* "ERROR: 9-path image <path> malformed"<br>
|
||||
* Line 2 and 3 are taken as-is while line 4 is ignored (it repeats with<br>
|
||||
* 'ERROR: failure processing <path>)
|
||||
*/
|
||||
private final static Pattern sPattern7Line1 = Pattern.compile(
|
||||
"^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$"); //$NON-NLS-1$
|
||||
|
||||
private final static Pattern sPattern8Line1 = Pattern.compile(
|
||||
"^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
|
||||
|
||||
/** SAX Parser factory. */
|
||||
private SAXParserFactory mParserFactory;
|
||||
|
||||
/**
|
||||
* Base Resource Delta Visitor to handle XML error
|
||||
*/
|
||||
protected static class BaseDeltaVisitor implements XmlErrorListener {
|
||||
|
||||
/** The Xml builder used to validate XML correctness. */
|
||||
protected BaseBuilder mBuilder;
|
||||
|
||||
/**
|
||||
* XML error flag. if true, we keep parsing the ResourceDelta but the
|
||||
* compilation will not happen (we're putting markers)
|
||||
*/
|
||||
public boolean mXmlError = false;
|
||||
|
||||
public BaseDeltaVisitor(BaseBuilder builder) {
|
||||
mBuilder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a matching Source folder for the current path. This checkds if the current path
|
||||
* leads to, or is a source folder.
|
||||
* @param sourceFolders The list of source folders
|
||||
* @param pathSegments The segments of the current path
|
||||
* @return The segments of the source folder, or null if no match was found
|
||||
*/
|
||||
protected static String[] findMatchingSourceFolder(ArrayList<IPath> sourceFolders,
|
||||
String[] pathSegments) {
|
||||
|
||||
for (IPath p : sourceFolders) {
|
||||
// check if we are inside one of those source class path
|
||||
|
||||
// get the segments
|
||||
String[] srcSegments = p.segments();
|
||||
|
||||
// compare segments. We want the path of the resource
|
||||
// we're visiting to be
|
||||
boolean valid = true;
|
||||
int segmentCount = pathSegments.length;
|
||||
|
||||
for (int i = 0 ; i < segmentCount; i++) {
|
||||
String s1 = pathSegments[i];
|
||||
String s2 = srcSegments[i];
|
||||
|
||||
if (s1.equalsIgnoreCase(s2) == false) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
// this folder, or one of this children is a source
|
||||
// folder!
|
||||
// we return its segments
|
||||
return srcSegments;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent when an XML error is detected.
|
||||
* @see XmlErrorListener
|
||||
*/
|
||||
public void errorFound() {
|
||||
mXmlError = true;
|
||||
}
|
||||
}
|
||||
|
||||
public BaseBuilder() {
|
||||
super();
|
||||
mParserFactory = SAXParserFactory.newInstance();
|
||||
|
||||
// FIXME when the compiled XML support for namespace is in, set this to true.
|
||||
mParserFactory.setNamespaceAware(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an Xml file for validity. Errors/warnings will be marked on the
|
||||
* file
|
||||
* @param resource the resource to check
|
||||
* @param visitor a valid resource delta visitor
|
||||
*/
|
||||
protected final void checkXML(IResource resource, BaseDeltaVisitor visitor) {
|
||||
|
||||
// first make sure this is an xml file
|
||||
if (resource instanceof IFile) {
|
||||
IFile file = (IFile)resource;
|
||||
|
||||
// remove previous markers
|
||||
removeMarkersFromFile(file, AndroidConstants.MARKER_XML);
|
||||
|
||||
// create the error handler
|
||||
XmlErrorHandler reporter = new XmlErrorHandler(file, visitor);
|
||||
try {
|
||||
// parse
|
||||
getParser().parse(file.getContents(), reporter);
|
||||
} catch (Exception e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SAXParserFactory, instantiating it first if it's not already
|
||||
* created.
|
||||
* @return the SAXParserFactory object
|
||||
* @throws ParserConfigurationException
|
||||
* @throws SAXException
|
||||
*/
|
||||
protected final SAXParser getParser() throws ParserConfigurationException,
|
||||
SAXException {
|
||||
return mParserFactory.newSAXParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a marker to the current project.
|
||||
* @param file the file to be marked
|
||||
* @param markerId The id of the marker to add.
|
||||
* @param message the message associated with the mark
|
||||
* @param severity the severity of the marker.
|
||||
*/
|
||||
protected final void markProject(String markerId, String message, int severity) {
|
||||
BaseProjectHelper.addMarker(getProject(), markerId, message, severity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes markers from a file.
|
||||
* @param file The file from which to delete the markers.
|
||||
* @param markerId The id of the markers to remove. If null, all marker of
|
||||
* type <code>IMarker.PROBLEM</code> will be removed.
|
||||
*/
|
||||
protected final void removeMarkersFromFile(IFile file, String markerId) {
|
||||
try {
|
||||
if (file.exists()) {
|
||||
file.deleteMarkers(markerId, true, IResource.DEPTH_ZERO);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
String msg = String.format(Messages.Marker_Delete_Error, markerId, file.toString());
|
||||
AdtPlugin.printErrorToConsole(getProject(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes markers from a container and its children.
|
||||
* @param container The container from which to delete the markers.
|
||||
* @param markerId The id of the markers to remove. If null, all marker of
|
||||
* type <code>IMarker.PROBLEM</code> will be removed.
|
||||
*/
|
||||
protected final void removeMarkersFromContainer(IContainer folder,
|
||||
String markerId) {
|
||||
try {
|
||||
if (folder.exists()) {
|
||||
folder.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
String msg = String.format(Messages.Marker_Delete_Error, markerId, folder.toString());
|
||||
AdtPlugin.printErrorToConsole(getProject(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes markers from a project and its children.
|
||||
* @param project The project from which to delete the markers
|
||||
* @param markerId The id of the markers to remove. If null, all marker of
|
||||
* type <code>IMarker.PROBLEM</code> will be removed.
|
||||
*/
|
||||
protected final static void removeMarkersFromProject(IProject project,
|
||||
String markerId) {
|
||||
try {
|
||||
if (project.exists()) {
|
||||
project.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
String msg = String.format(Messages.Marker_Delete_Error, markerId, project.getName());
|
||||
AdtPlugin.printErrorToConsole(project, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stderr output of a process and return when the process is done.
|
||||
* @param process The process to get the ouput from
|
||||
* @param results The array to store the stderr output
|
||||
* @return the process return code.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
protected final int grabProcessOutput(final Process process,
|
||||
final ArrayList<String> results)
|
||||
throws InterruptedException {
|
||||
// Due to the limited buffer size on windows for the standard io (stderr, stdout), we
|
||||
// *need* to read both stdout and stderr all the time. If we don't and a process output
|
||||
// a large amount, this could deadlock the process.
|
||||
|
||||
// read the lines as they come. if null is returned, it's
|
||||
// because the process finished
|
||||
new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
// create a buffer to read the stderr output
|
||||
InputStreamReader is = new InputStreamReader(process.getErrorStream());
|
||||
BufferedReader errReader = new BufferedReader(is);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = errReader.readLine();
|
||||
if (line != null) {
|
||||
results.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
InputStreamReader is = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader outReader = new BufferedReader(is);
|
||||
|
||||
IProject project = getProject();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = outReader.readLine();
|
||||
if (line != null) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
|
||||
project, line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
}.start();
|
||||
|
||||
// get the return code from the process
|
||||
return process.waitFor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the output of aapt and mark the incorrect file with error markers
|
||||
*
|
||||
* @param results the output of aapt
|
||||
* @param project the project containing the file to mark
|
||||
* @return true if the parsing failed, false if success.
|
||||
*/
|
||||
protected final boolean parseAaptOutput(ArrayList<String> results,
|
||||
IProject project) {
|
||||
// nothing to parse? just return false;
|
||||
if (results.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the root of the project so that we can make IFile from full
|
||||
// file path
|
||||
String osRoot = project.getLocation().toOSString();
|
||||
|
||||
Matcher m;
|
||||
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
String p = results.get(i);
|
||||
|
||||
m = sPattern0Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
// we ignore those (as this is an ignore message from aapt)
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern1Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String lineStr = m.group(1);
|
||||
String msg = m.group(2);
|
||||
|
||||
// get the matcher for the next line.
|
||||
m = getNextLineMatcher(results, ++i, sPattern1Line2);
|
||||
if (m == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String location = m.group(1);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project, IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// this needs to be tested before Pattern2 since they both start with 'ERROR:'
|
||||
m = sPattern7Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String location = m.group(1);
|
||||
String msg = p; // default msg is the line in case we don't find anything else
|
||||
|
||||
if (++i < results.size()) {
|
||||
msg = results.get(i).trim();
|
||||
if (++i < results.size()) {
|
||||
msg = msg + " - " + results.get(i).trim(); //$NON-NLS-1$
|
||||
|
||||
// skip the next line
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// display the error
|
||||
if (checkAndMark(location, null, msg, osRoot, project,
|
||||
IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern2Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
// get the msg
|
||||
String msg = m.group(1);
|
||||
|
||||
// get the matcher for the next line.
|
||||
m = getNextLineMatcher(results, ++i, sPattern2Line2);
|
||||
if (m == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String location = m.group(1);
|
||||
String lineStr = m.group(2);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project, IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern3Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String location = m.group(1);
|
||||
String lineStr = m.group(2);
|
||||
String msg = m.group(3);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project, IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern4Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
// get the filename.
|
||||
String location = m.group(1);
|
||||
|
||||
// get the matcher for the next line.
|
||||
m = getNextLineMatcher(results, ++i, sPattern4Line2);
|
||||
if (m == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String msg = m.group(1);
|
||||
String lineStr = m.group(2);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project, IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern5Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String location = m.group(1);
|
||||
String lineStr = m.group(2);
|
||||
String msg = m.group(3);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project,IMarker.SEVERITY_WARNING) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern6Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String location = m.group(1);
|
||||
String lineStr = m.group(2);
|
||||
String msg = m.group(3);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, lineStr, msg, osRoot,
|
||||
project, IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
m = sPattern8Line1.matcher(p);
|
||||
if (m.matches()) {
|
||||
String location = m.group(2);
|
||||
String msg = m.group(1);
|
||||
|
||||
// check the values and attempt to mark the file.
|
||||
if (checkAndMark(location, null, msg, osRoot, project,
|
||||
IMarker.SEVERITY_ERROR) == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success, go to the next line
|
||||
continue;
|
||||
}
|
||||
|
||||
// invalid line format, flag as error, and bail
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Saves a String property into the persistent storage of the project.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param value the value to save
|
||||
* @return true if the save succeeded.
|
||||
*/
|
||||
protected boolean saveProjectStringProperty(String propertyName, String value) {
|
||||
IProject project = getProject();
|
||||
return ProjectHelper.saveStringProperty(project, propertyName, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads a String property from the persistent storage of the project.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @return the property value or null if it was not found.
|
||||
*/
|
||||
protected String loadProjectStringProperty(String propertyName) {
|
||||
IProject project = getProject();
|
||||
return ProjectHelper.loadStringProperty(project, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a property into the persistent storage of the project.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param value the value to save
|
||||
* @return true if the save succeeded.
|
||||
*/
|
||||
protected boolean saveProjectBooleanProperty(String propertyName, boolean value) {
|
||||
IProject project = getProject();
|
||||
return ProjectHelper.saveStringProperty(project, propertyName, Boolean.toString(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a boolean property from the persistent storage of the project.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param defaultValue The default value to return if the property was not found.
|
||||
* @return the property value or the default value if the property was not found.
|
||||
*/
|
||||
protected boolean loadProjectBooleanProperty(String propertyName, boolean defaultValue) {
|
||||
IProject project = getProject();
|
||||
return ProjectHelper.loadBooleanProperty(project, propertyName, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the path of a resource into the persistent storate of the project.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param resource the resource which path is saved.
|
||||
* @return true if the save succeeded
|
||||
*/
|
||||
protected boolean saveProjectResourceProperty(String propertyName, IResource resource) {
|
||||
return ProjectHelper.saveResourceProperty(getProject(), propertyName, resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the path of a resource from the persistent storage of the project, and returns the
|
||||
* corresponding IResource object.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @return The corresponding IResource object (or children interface) or null
|
||||
*/
|
||||
protected IResource loadProjectResourceProperty(String propertyName) {
|
||||
IProject project = getProject();
|
||||
return ProjectHelper.loadResourceProperty(project, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parameters gotten from the error output are valid, and mark
|
||||
* the file with an AAPT marker.
|
||||
* @param location
|
||||
* @param lineStr
|
||||
* @param message
|
||||
* @param root The root directory of the project, in OS specific format.
|
||||
* @param project
|
||||
* @param severity The severity of the marker to put (IMarker.SEVERITY_*)
|
||||
* @return true if the parameters were valid and the file was marked
|
||||
* sucessfully.
|
||||
*
|
||||
* @see IMarker
|
||||
*/
|
||||
private final boolean checkAndMark(String location, String lineStr,
|
||||
String message, String root, IProject project, int severity) {
|
||||
// check this is in fact a file
|
||||
File f = new File(location);
|
||||
if (f.exists() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the line number
|
||||
int line = -1; // default value for error with no line.
|
||||
|
||||
if (lineStr != null) {
|
||||
try {
|
||||
line = Integer.parseInt(lineStr);
|
||||
} catch (NumberFormatException e) {
|
||||
// looks like the string we extracted wasn't a valid
|
||||
// file number. Parsing failed and we return true
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// add the marker
|
||||
IResource f2 = getResourceFromFullPath(location, root, project);
|
||||
if (f2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if there's a similar marker already, since aapt is launched twice
|
||||
boolean markerAlreadyExists = false;
|
||||
try {
|
||||
IMarker[] markers = f2.findMarkers(AndroidConstants.MARKER_AAPT, true,
|
||||
IResource.DEPTH_ZERO);
|
||||
|
||||
for (IMarker marker : markers) {
|
||||
int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
|
||||
if (tmpLine != line) {
|
||||
break;
|
||||
}
|
||||
|
||||
int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1);
|
||||
if (tmpSeverity != severity) {
|
||||
break;
|
||||
}
|
||||
|
||||
String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
|
||||
if (tmpMsg == null || tmpMsg.equals(message) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if we're here, all the marker attributes are equals, we found it
|
||||
// and exit
|
||||
markerAlreadyExists = true;
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (CoreException e) {
|
||||
// if we couldn't get the markers, then we just mark the file again
|
||||
// (since markerAlreadyExists is initialized to false, we do nothing)
|
||||
}
|
||||
|
||||
if (markerAlreadyExists == false) {
|
||||
if (line != -1) {
|
||||
BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, line,
|
||||
severity);
|
||||
} else {
|
||||
BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, severity);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matching matcher for the next line
|
||||
* @param lines The array of lines
|
||||
* @param nextIndex The index of the next line
|
||||
* @param pattern The pattern to match
|
||||
* @return null if error or no match, the matcher otherwise.
|
||||
*/
|
||||
private final Matcher getNextLineMatcher(ArrayList<String> lines,
|
||||
int nextIndex, Pattern pattern) {
|
||||
// unless we can't, because we reached the last line
|
||||
if (nextIndex == lines.size()) {
|
||||
// we expected a 2nd line, so we flag as error
|
||||
// and we bail
|
||||
return null;
|
||||
}
|
||||
|
||||
Matcher m = pattern.matcher(lines.get(nextIndex));
|
||||
if (m.matches()) {
|
||||
return m;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IResource getResourceFromFullPath(String filename, String root,
|
||||
IProject project) {
|
||||
if (filename.startsWith(root)) {
|
||||
String file = filename.substring(root.length());
|
||||
|
||||
// get the resource
|
||||
IResource r = project.findMember(file);
|
||||
|
||||
// if the resource is valid, we add the marker
|
||||
if (r.exists()) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of external jar files used by the project.
|
||||
* @return an array of OS-specific absolute file paths
|
||||
*/
|
||||
protected final String[] getExternalJars() {
|
||||
// get the current project
|
||||
IProject project = getProject();
|
||||
|
||||
// get a java project from it
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
|
||||
|
||||
ArrayList<String> oslibraryList = new ArrayList<String>();
|
||||
IClasspathEntry[] classpaths = javaProject.readRawClasspath();
|
||||
if (classpaths != null) {
|
||||
for (IClasspathEntry e : classpaths) {
|
||||
if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY ||
|
||||
e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
|
||||
// if this is a classpath variable reference, we resolve it.
|
||||
if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
|
||||
e = JavaCore.getResolvedClasspathEntry(e);
|
||||
}
|
||||
|
||||
// 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;
|
||||
IResource resource = wsRoot.findMember(path);
|
||||
if (resource != null && resource.exists() &&
|
||||
resource.getType() == IResource.FILE) {
|
||||
local = true;
|
||||
oslibraryList.add(resource.getLocation().toOSString());
|
||||
}
|
||||
|
||||
if (local == false) {
|
||||
// if the jar path doesn't match a workspace resource,
|
||||
// then we get an OSString and check if this links to a valid file.
|
||||
String osFullPath = path.toOSString();
|
||||
|
||||
File f = new File(osFullPath);
|
||||
if (f.exists()) {
|
||||
oslibraryList.add(osFullPath);
|
||||
} else {
|
||||
String message = String.format( Messages.Couldnt_Locate_s_Error,
|
||||
path);
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
|
||||
project, message);
|
||||
|
||||
// Also put a warning marker on the project
|
||||
markProject(AdtConstants.MARKER_ADT, message,
|
||||
IMarker.SEVERITY_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return oslibraryList.toArray(new String[oslibraryList.size()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* Wrapper to access dex.jar through reflection.
|
||||
* <p/>Since there is no proper api to call the method in the dex library, this wrapper is going
|
||||
* to access it through reflection.
|
||||
*/
|
||||
public final class DexWrapper {
|
||||
|
||||
private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$
|
||||
private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$
|
||||
private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$
|
||||
|
||||
private final static String MAIN_RUN = "run"; //$NON-NLS-1$
|
||||
|
||||
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;
|
||||
|
||||
private Constructor<?> mArgConstructor;
|
||||
private Field mArgOutName;
|
||||
private Field mArgVerbose;
|
||||
private Field mArgJarOutput;
|
||||
private Field mArgFileNames;
|
||||
|
||||
private Field mConsoleOut;
|
||||
private Field mConsoleErr;
|
||||
|
||||
/**
|
||||
* Loads the dex library from a file path. The loaded library can be used with the
|
||||
* {@link DexWrapper} object returned by {@link #getWrapper()}
|
||||
* @param osFilepath the location of the dex.jar file.
|
||||
* @return an IStatus indicating the result of the load.
|
||||
*/
|
||||
public static synchronized IStatus loadDex(String osFilepath) {
|
||||
try {
|
||||
sWrapper = null;
|
||||
|
||||
File f = new File(osFilepath);
|
||||
if (f.isFile() == false) {
|
||||
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format(
|
||||
Messages.DexWrapper_s_does_not_exists, osFilepath));
|
||||
}
|
||||
URL url = f.toURL();
|
||||
|
||||
URLClassLoader loader = new URLClassLoader(new URL[] { url },
|
||||
DexWrapper.class.getClassLoader());
|
||||
|
||||
// get the classes.
|
||||
Class<?> mainClass = loader.loadClass(DEX_MAIN);
|
||||
Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
|
||||
Class<?> argClass = loader.loadClass(DEX_ARGS);
|
||||
|
||||
sWrapper = new DexWrapper(mainClass, argClass, consoleClass);
|
||||
|
||||
return Status.OK_STATUS;
|
||||
} catch (MalformedURLException e) {
|
||||
// really this should not happen.
|
||||
return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
|
||||
} catch (CoreException e) {
|
||||
return e.getStatus();
|
||||
} finally {
|
||||
if (sWrapper == null) {
|
||||
sLoadStatus = LoadStatus.FAILED;
|
||||
} else {
|
||||
sLoadStatus = LoadStatus.LOADED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the loaded dex wrapper.
|
||||
*/
|
||||
public static synchronized void unloadDex() {
|
||||
sWrapper = null;
|
||||
sLoadStatus = LoadStatus.LOADING;
|
||||
}
|
||||
|
||||
public static synchronized DexWrapper getWrapper() {
|
||||
return sWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link LoadStatus}.
|
||||
*/
|
||||
public static synchronized LoadStatus getStatus() {
|
||||
return sLoadStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the dex command.
|
||||
* @param osOutFilePath the OS path to the outputfile (classes.dex
|
||||
* @param osFilenames list of input source files (.class and .jar files)
|
||||
* @param verbose verbose mode.
|
||||
* @param outStream the stdout console
|
||||
* @param errStream the stderr console
|
||||
* @return the integer return code of com.android.dx.command.dexer.Main.run()
|
||||
* @throws CoreException
|
||||
*/
|
||||
public synchronized int run(String osOutFilePath, String[] osFilenames,
|
||||
boolean verbose, PrintStream outStream, PrintStream errStream) throws CoreException {
|
||||
|
||||
try {
|
||||
// set the stream
|
||||
mConsoleErr.set(null /* obj: static field */, errStream);
|
||||
mConsoleOut.set(null /* obj: static field */, outStream);
|
||||
|
||||
// create the Arguments object.
|
||||
Object args = mArgConstructor.newInstance();
|
||||
mArgOutName.set(args, osOutFilePath);
|
||||
mArgFileNames.set(args, osFilenames);
|
||||
mArgJarOutput.set(args, false);
|
||||
mArgVerbose.set(args, verbose);
|
||||
|
||||
// call the run method
|
||||
Object res = mRunMethod.invoke(null /* obj: static method */, args);
|
||||
|
||||
if (res instanceof Integer) {
|
||||
return ((Integer)res).intValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e));
|
||||
} catch (InstantiationException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e));
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e));
|
||||
}
|
||||
}
|
||||
|
||||
private DexWrapper(Class<?> mainClass, Class<?> argClass, Class<?> consoleClass)
|
||||
throws CoreException {
|
||||
try {
|
||||
// now get the fields/methods we need
|
||||
mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);
|
||||
|
||||
mArgConstructor = argClass.getConstructor();
|
||||
mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
|
||||
mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
|
||||
mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
|
||||
mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
|
||||
|
||||
mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
|
||||
mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$
|
||||
|
||||
} catch (SecurityException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e));
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new CoreException(createErrorStatus(
|
||||
Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e));
|
||||
}
|
||||
}
|
||||
|
||||
private static IStatus createErrorStatus(String message, Exception e) {
|
||||
AdtPlugin.log(e, message);
|
||||
AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message);
|
||||
|
||||
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
|
||||
package com.android.ide.eclipse.adt.build;
|
||||
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
public class Messages extends NLS {
|
||||
private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.build.build_messages"; //$NON-NLS-1$
|
||||
|
||||
public static String AAPT_Error;
|
||||
|
||||
public static String AAPT_Exec_Error;
|
||||
|
||||
public static String Added_s_s_Needs_Updating;
|
||||
|
||||
public static String AIDL_Exec_Error;
|
||||
|
||||
public static String AIDL_Java_Conflict;
|
||||
|
||||
public static String ApkBuilder_Certificate_Expired_on_s;
|
||||
|
||||
public static String ApkBuilder_JAVA_HOME_is_s;
|
||||
|
||||
public static String ApkBuilder_Packaging_s;
|
||||
|
||||
public static String ApkBuilder_Packaging_s_into_s;
|
||||
|
||||
public static String ApkBuilder_s_Conflict_with_file_s;
|
||||
|
||||
public static String ApkBuilder_Signing_Key_Creation_s;
|
||||
|
||||
public static String ApkBuilder_Unable_To_Gey_Key;
|
||||
|
||||
public static String ApkBuilder_UnableBuild_Dex_Not_loaded;
|
||||
|
||||
public static String ApkBuilder_Update_or_Execute_manually_s;
|
||||
|
||||
public static String ApkBuilder_Using_Default_Key;
|
||||
|
||||
public static String ApkBuilder_Using_s_To_Sign;
|
||||
|
||||
public static String Checking_Package_Change;
|
||||
|
||||
public static String Compiler_Compliance_Error;
|
||||
|
||||
public static String Couldnt_Locate_s_Error;
|
||||
|
||||
public static String Dalvik_Error_d;
|
||||
|
||||
public static String Dalvik_Error_s;
|
||||
|
||||
public static String Delete_Obsolete_Error;
|
||||
|
||||
public static String DexWrapper_Dex_Loader;
|
||||
|
||||
public static String DexWrapper_Failed_to_load_s;
|
||||
|
||||
public static String DexWrapper_s_does_not_exists;
|
||||
|
||||
public static String DexWrapper_SecuryEx_Unable_To_Find_API;
|
||||
|
||||
public static String DexWrapper_SecuryEx_Unable_To_Find_Field;
|
||||
|
||||
public static String DexWrapper_SecuryEx_Unable_To_Find_Method;
|
||||
|
||||
public static String DexWrapper_Unable_To_Execute_Dex_s;
|
||||
|
||||
public static String DX_Jar_Error;
|
||||
|
||||
public static String Failed_To_Get_Output;
|
||||
|
||||
public static String Final_Archive_Error_s;
|
||||
|
||||
public static String Incompatible_VM_Warning;
|
||||
|
||||
public static String Marker_Delete_Error;
|
||||
|
||||
public static String No_SDK_Setup_Error;
|
||||
|
||||
public static String Nothing_To_Compile;
|
||||
|
||||
public static String Output_Missing;
|
||||
|
||||
public static String Package_s_Doesnt_Exist_Error;
|
||||
|
||||
public static String Preparing_Generated_Files;
|
||||
|
||||
public static String Project_Has_Errors;
|
||||
|
||||
public static String Refreshing_Res;
|
||||
|
||||
public static String Removing_Generated_Classes;
|
||||
|
||||
public static String Requires_1_5_Error;
|
||||
|
||||
public static String Requires_Class_Compatibility_5;
|
||||
|
||||
public static String Requires_Compiler_Compliance_5;
|
||||
|
||||
public static String Requires_Source_Compatibility_5;
|
||||
|
||||
public static String s_Contains_Xml_Error;
|
||||
|
||||
public static String s_Doesnt_Declare_Package_Error;
|
||||
|
||||
public static String s_File_Missing;
|
||||
|
||||
public static String s_Missing_Repackaging;
|
||||
|
||||
public static String s_Modified_Manually_Recreating_s;
|
||||
|
||||
public static String s_Modified_Recreating_s;
|
||||
|
||||
public static String s_Removed_Recreating_s;
|
||||
|
||||
public static String s_Removed_s_Needs_Updating;
|
||||
|
||||
public static String Start_Full_Apk_Build;
|
||||
|
||||
public static String Start_Full_Pre_Compiler;
|
||||
|
||||
public static String Start_Inc_Apk_Build;
|
||||
|
||||
public static String Start_Inc_Pre_Compiler;
|
||||
|
||||
public static String Unparsed_AAPT_Errors;
|
||||
|
||||
public static String Unparsed_AIDL_Errors;
|
||||
|
||||
public static String Xml_Error;
|
||||
static {
|
||||
// initialize resource bundle
|
||||
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
|
||||
}
|
||||
|
||||
private Messages() {
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Resource Delta visitor for the pre-compiler.
|
||||
*/
|
||||
class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
|
||||
IResourceDeltaVisitor {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* this flag is true, then we know we'll have to compile the resources
|
||||
* into R.java
|
||||
*/
|
||||
private boolean mCompileResources = false;
|
||||
|
||||
/**
|
||||
* In Resource folder flag. This allows us to know if we're in the
|
||||
* resource folder.
|
||||
*/
|
||||
private boolean mInRes = false;
|
||||
|
||||
/**
|
||||
* In Source folder flag. This allows us to know if we're in a source
|
||||
* folder.
|
||||
*/
|
||||
private boolean mInSrc = false;
|
||||
|
||||
/** 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);
|
||||
mSourceFolders = sourceFolders;
|
||||
}
|
||||
|
||||
public boolean getCompileResources() {
|
||||
return mCompileResources;
|
||||
}
|
||||
|
||||
public ArrayList<IFile> getAidlToCompile() {
|
||||
return mAidlToCompile;
|
||||
}
|
||||
|
||||
public ArrayList<IFile> getAidlToRemove() {
|
||||
return mAidlToRemove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the manifest file was parsed/checked for error during the resource delta
|
||||
* visiting.
|
||||
*/
|
||||
public boolean getCheckedManifestXml() {
|
||||
return mCheckedManifestXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the manifest package if the manifest was checked.
|
||||
* <p/>
|
||||
* This can return null in two cases:
|
||||
* <ul>
|
||||
* <li>The manifest was not part of the resource change delta, and the manifest was
|
||||
* not checked/parsed ({@link #getCheckedManifestXml()} returns <code>false</code>)</li>
|
||||
* <li>The manifest was parsed ({@link #getCheckedManifestXml()} returns <code>true</code>),
|
||||
* but the package declaration is missing</li>
|
||||
* </ul>
|
||||
* @return the manifest package or null.
|
||||
*/
|
||||
public String getManifestPackage() {
|
||||
return mJavaPackage;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.core.resources.IResourceDeltaVisitor
|
||||
* #visit(org.eclipse.core.resources.IResourceDelta)
|
||||
*/
|
||||
public boolean visit(IResourceDelta delta) throws CoreException {
|
||||
// we are only going to look for changes in res/, source folders and in
|
||||
// AndroidManifest.xml since the delta visitor goes through the main
|
||||
// folder before its children we can check when the path segment
|
||||
// count is 2 (format will be /$Project/folder) and make sure we are
|
||||
// processing res/, source folders or AndroidManifest.xml
|
||||
|
||||
IResource resource = delta.getResource();
|
||||
IPath path = resource.getFullPath();
|
||||
String[] segments = path.segments();
|
||||
|
||||
// since the delta visitor also visits the root we return true if
|
||||
// segments.length = 1
|
||||
if (segments.length == 1) {
|
||||
return true;
|
||||
} else if (segments.length == 2) {
|
||||
// if we are at an item directly under the root directory,
|
||||
// then we are not yet in a source or resource folder
|
||||
mInRes = mInSrc = false;
|
||||
|
||||
if (AndroidConstants.FD_RESOURCES.equalsIgnoreCase(segments[1])) {
|
||||
// this is the resource folder that was modified. we want to
|
||||
// see its content.
|
||||
|
||||
// since we're going to visit its children next, we set the
|
||||
// flag
|
||||
mInRes = true;
|
||||
mInSrc = false;
|
||||
return true;
|
||||
} else if (AndroidConstants.FN_ANDROID_MANIFEST.equalsIgnoreCase(segments[1])) {
|
||||
// any change in the manifest could trigger a new R.java
|
||||
// class, so we don't need to check the delta kind
|
||||
if (delta.getKind() != IResourceDelta.REMOVED) {
|
||||
// parse the manifest for errors
|
||||
AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(
|
||||
(IFile)resource, this);
|
||||
|
||||
if (parser != null) {
|
||||
mJavaPackage = parser.getPackage();
|
||||
}
|
||||
|
||||
mCheckedManifestXml = true;
|
||||
}
|
||||
mCompileResources = true;
|
||||
|
||||
// we don't want to go to the children, not like they are
|
||||
// any for this resource anyway.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// folder.
|
||||
// This is due to not all source folder being src/. Some could be
|
||||
// something/somethingelse/src/
|
||||
|
||||
// so first we test if we already know we are in a source or
|
||||
// resource folder.
|
||||
|
||||
if (mInSrc) {
|
||||
// if we are in the res folder, we are looking for the following changes:
|
||||
// - added/removed/modified aidl files.
|
||||
// - missing R.java file
|
||||
|
||||
// if the resource is a folder, we just go straight to the children
|
||||
if (resource.getType() == IResource.FOLDER) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resource.getType() != IResource.FILE) {
|
||||
return false;
|
||||
}
|
||||
IFile file = (IFile)resource;
|
||||
|
||||
// get the modification kind
|
||||
int kind = delta.getKind();
|
||||
|
||||
if (kind == IResourceDelta.ADDED) {
|
||||
// we only care about added files (inside the source folders), if they
|
||||
// are aidl files.
|
||||
|
||||
// get the extension of the resource
|
||||
String ext = resource.getFileExtension();
|
||||
|
||||
if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) {
|
||||
// look for an already existing matching java file
|
||||
String javaName = resource.getName().replaceAll(
|
||||
AndroidConstants.RE_AIDL_EXT,
|
||||
AndroidConstants.DOT_JAVA);
|
||||
|
||||
// get the parent container
|
||||
IContainer ic = resource.getParent();
|
||||
|
||||
IFile javaFile = ic.getFile(new Path(javaName));
|
||||
if (javaFile != null && javaFile.exists()) {
|
||||
// check if that file was generated by the plugin. Normally those files are
|
||||
// deleted automatically, but it's better to check.
|
||||
String aidlPath = ProjectHelper.loadStringProperty(javaFile,
|
||||
PreCompilerBuilder.PROPERTY_ANDROID_GENERATED);
|
||||
if (aidlPath == null) {
|
||||
// mark the aidl file that it cannot be compile just yet
|
||||
ProjectHelper.saveBooleanProperty(file,
|
||||
PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, true);
|
||||
}
|
||||
|
||||
// we add it anyway so that we can try to compile it every time.
|
||||
mAidlToCompile.add(file);
|
||||
|
||||
} else {
|
||||
// the java file doesn't exist, we can safely add the file to the list
|
||||
// of files to compile.
|
||||
mAidlToCompile.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the filename
|
||||
String fileName = segments[segments.length - 1];
|
||||
|
||||
boolean outputMessage = false;
|
||||
|
||||
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
|
||||
// package change, or an aidl that was removed, but the only thing
|
||||
// that will happen is that we'll have an extra build. Not much of a problem.
|
||||
mCompileResources = true;
|
||||
|
||||
// we want a warning
|
||||
outputMessage = true;
|
||||
} else {
|
||||
|
||||
// get the extension of the resource
|
||||
String ext = resource.getFileExtension();
|
||||
|
||||
if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) {
|
||||
if (kind == IResourceDelta.REMOVED) {
|
||||
mAidlToRemove.add(file);
|
||||
} else {
|
||||
mAidlToCompile.add(file);
|
||||
}
|
||||
} else {
|
||||
if (kind == IResourceDelta.REMOVED) {
|
||||
// the file has been removed. we need to check it's a java file and that
|
||||
// there's a matching aidl file. We can't check its persistent storage
|
||||
// anymore.
|
||||
if (AndroidConstants.EXT_JAVA.equalsIgnoreCase(ext)) {
|
||||
String aidlFile = resource.getName().replaceAll(
|
||||
AndroidConstants.RE_JAVA_EXT,
|
||||
AndroidConstants.DOT_AIDL);
|
||||
|
||||
// get the parent container
|
||||
IContainer ic = resource.getParent();
|
||||
|
||||
IFile f = ic.getFile(new Path(aidlFile));
|
||||
if (f != null && f.exists() ) {
|
||||
// make sure that the aidl file is not in conflict anymore, in
|
||||
// case the java file was not generated by us.
|
||||
if (ProjectHelper.loadBooleanProperty(f,
|
||||
PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, false)) {
|
||||
ProjectHelper.saveBooleanProperty(f,
|
||||
PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, false);
|
||||
} else {
|
||||
outputMessage = true;
|
||||
}
|
||||
mAidlToCompile.add(f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// check if it's an android generated java file.
|
||||
IResource aidlSource = ProjectHelper.loadResourceProperty(
|
||||
file, PreCompilerBuilder.PROPERTY_ANDROID_GENERATED);
|
||||
|
||||
if (aidlSource != null && aidlSource.exists() &&
|
||||
aidlSource.getType() == IResource.FILE) {
|
||||
// it looks like this was a java file created from an aidl file.
|
||||
// we need to add the aidl file to the list of aidl file to compile
|
||||
mAidlToCompile.add((IFile)aidlSource);
|
||||
outputMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outputMessage) {
|
||||
if (kind == IResourceDelta.REMOVED) {
|
||||
// We pring an error just so that it's red, but it's just a warning really.
|
||||
String msg = String.format(Messages.s_Removed_Recreating_s, fileName);
|
||||
AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg);
|
||||
} else if (kind == IResourceDelta.CHANGED) {
|
||||
// the file was modified manually! we can't allow it.
|
||||
String msg = String.format(Messages.s_Modified_Manually_Recreating_s, fileName);
|
||||
AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
// no children.
|
||||
return false;
|
||||
} else if (mInRes) {
|
||||
// if we are in the res folder, we are looking for the following
|
||||
// changes:
|
||||
// - added/removed/modified xml files.
|
||||
// - added/removed files of any other type
|
||||
|
||||
// if the resource is a folder, we just go straight to the
|
||||
// children
|
||||
if (resource.getType() == IResource.FOLDER) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the extension of the resource
|
||||
String ext = resource.getFileExtension();
|
||||
int kind = delta.getKind();
|
||||
|
||||
String p = resource.getProjectRelativePath().toString();
|
||||
String message = null;
|
||||
switch (kind) {
|
||||
case IResourceDelta.CHANGED:
|
||||
// display verbose message
|
||||
message = String.format(Messages.s_Modified_Recreating_s, p,
|
||||
AndroidConstants.FN_RESOURCE_CLASS);
|
||||
break;
|
||||
case IResourceDelta.ADDED:
|
||||
// display verbose message
|
||||
message = String.format(Messages.Added_s_s_Needs_Updating, p,
|
||||
AndroidConstants.FN_RESOURCE_CLASS);
|
||||
break;
|
||||
case IResourceDelta.REMOVED:
|
||||
// display verbose message
|
||||
message = String.format(Messages.s_Removed_s_Needs_Updating, p,
|
||||
AndroidConstants.FN_RESOURCE_CLASS);
|
||||
break;
|
||||
}
|
||||
if (message != null) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
|
||||
mBuilder.getProject(), message);
|
||||
}
|
||||
|
||||
if (AndroidConstants.EXT_XML.equalsIgnoreCase(ext)) {
|
||||
if (kind != IResourceDelta.REMOVED) {
|
||||
// check xml Validity
|
||||
mBuilder.checkXML(resource, this);
|
||||
}
|
||||
|
||||
// if we are going through this resource, it was modified
|
||||
// somehow.
|
||||
// we don't care if it was an added/removed/changed event
|
||||
mCompileResources = true;
|
||||
return false;
|
||||
} else {
|
||||
// this is a non xml resource.
|
||||
if (kind == IResourceDelta.ADDED
|
||||
|| kind == IResourceDelta.REMOVED) {
|
||||
mCompileResources = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (resource instanceof IFolder) {
|
||||
// in this case we may be inside a folder that contains a source
|
||||
// folder.
|
||||
String[] sourceFolderSegments = findMatchingSourceFolder(mSourceFolders, segments);
|
||||
if (sourceFolderSegments != null) {
|
||||
// we have a match!
|
||||
mInRes = false;
|
||||
|
||||
// Check if the current folder is actually a source folder
|
||||
if (sourceFolderSegments.length == segments.length) {
|
||||
mInSrc = true;
|
||||
}
|
||||
|
||||
// and return true to visit the content, no matter what
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we're here, we are visiting another folder
|
||||
// like /$Project/bin/ for instance (we get notified for changes
|
||||
// in .class!)
|
||||
// This could also be another source folder and we have found
|
||||
// R.java in a previous source folder
|
||||
// We don't want to visit its children
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.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.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Resource manager builder whose only purpose is to refresh the resource folder
|
||||
* so that the other builder use an up to date version.
|
||||
*/
|
||||
public class ResourceManagerBuilder extends IncrementalProjectBuilder {
|
||||
|
||||
public static final String ID = "com.android.ide.eclipse.adt.ResourceManagerBuilder"; //$NON-NLS-1$
|
||||
|
||||
public ResourceManagerBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
// build() returns a list of project from which this project depends for future compilation.
|
||||
@SuppressWarnings("unchecked") //$NON-NLS-1$
|
||||
@Override
|
||||
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
|
||||
throws CoreException {
|
||||
// Get the project.
|
||||
IProject project = getProject();
|
||||
|
||||
// Clear the project of the generic markers
|
||||
BaseBuilder.removeMarkersFromProject(project, AdtConstants.MARKER_ADT);
|
||||
|
||||
// Check the compiler compliance level, displaying the error message
|
||||
// since this is the first builder.
|
||||
int res = ProjectHelper.checkCompilerCompliance(project);
|
||||
String errorMessage = null;
|
||||
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) {
|
||||
BaseProjectHelper.addMarker(project, AdtConstants.MARKER_ADT, errorMessage,
|
||||
IMarker.SEVERITY_ERROR);
|
||||
AdtPlugin.printErrorToConsole(project, errorMessage);
|
||||
}
|
||||
|
||||
// Check the preference to be sure we are supposed to refresh
|
||||
// the folders.
|
||||
if (AdtPlugin.getAutoResRefresh()) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
Messages.Refreshing_Res);
|
||||
|
||||
// refresh the res folder.
|
||||
IFolder resFolder = project.getFolder(
|
||||
AndroidConstants.WS_RESOURCES);
|
||||
resFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
|
||||
|
||||
// Also refresh the assets folder to make sure the ApkBuilder
|
||||
// will now it's changed and will force a new resource packaging.
|
||||
IFolder assetsFolder = project.getFolder(
|
||||
AndroidConstants.WS_ASSETS);
|
||||
assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
Start_Full_Apk_Build=Starting full Package build.
|
||||
Start_Full_Pre_Compiler=Starting full Pre Compiler.
|
||||
Start_Inc_Apk_Build=Starting incremental Package build: Checking resource changes.
|
||||
Start_Inc_Pre_Compiler=Starting incremental Pre Compiler: Checking resource changes.
|
||||
Xml_Error=Error in an XML file: aborting build.
|
||||
s_Missing_Repackaging=%1$s missing. Repackaging.
|
||||
Project_Has_Errors=Project contains error(s). Package Builder aborted.
|
||||
Failed_To_Get_Output=Failed to get project output folder\!
|
||||
Output_Missing=Output folder missing\! Make sure your project is configured properly.
|
||||
s_File_Missing=%1$s file missing\!
|
||||
Unparsed_AAPT_Errors=Unparsed aapt error(s)\! Check the console for output.
|
||||
Unparsed_AIDL_Errors=Unparsed aidl error\! Check the console for output.
|
||||
AAPT_Exec_Error=Error executing aapt. Please check aapt is present at %1$s
|
||||
Dalvik_Error_d=Conversion to Dalvik format failed with error %1$d
|
||||
DX_Jar_Error=Dx.jar is not found inside the plugin. Reinstall ADT\!
|
||||
Dalvik_Error_s=Conversion to Dalvik format failed: %1$s
|
||||
Incompatible_VM_Warning=Note: You may be using an incompatible virtual machine or class library.
|
||||
Requires_1_5_Error=This program requires JDK 1.5 compatibility.
|
||||
Final_Archive_Error_s=Error generating final archive: %1$s
|
||||
Marker_Delete_Error=Failed to delete marker '%1$s' for %2$s
|
||||
Couldnt_Locate_s_Error=Could not locate '%1$s'. This will not be added to the package.
|
||||
Compiler_Compliance_Error=Compiler compliance level not compatible: Build aborted.
|
||||
No_SDK_Setup_Error=SDK directory has not been setup. Please go to the Android preferences and enter the location of the SDK.
|
||||
s_Contains_Xml_Error=%1$s contains XML error: Build aborted.
|
||||
s_Doesnt_Declare_Package_Error=%1$s does not declare a Java package: Build aborted.
|
||||
Checking_Package_Change=Checking Java package value did not change...
|
||||
Package_s_Doesnt_Exist_Error=Package '%1$s' does not exist\!
|
||||
Preparing_Generated_Files=Preparing generated java files for update/creation.
|
||||
AAPT_Error='aapt' error. Pre Compiler Build aborted.
|
||||
Nothing_To_Compile=Nothing to pre compile\!
|
||||
Removing_Generated_Classes=Removing generated java classes.
|
||||
Delete_Obsolete_Error=Failed to delete obsolete %1$s, please delete it manually
|
||||
DexWrapper_Dex_Loader=Dex Loader
|
||||
AIDL_Java_Conflict=%1$s is in the way of %2$s, remove it or rename of one the files.
|
||||
AIDL_Exec_Error=Error executing aidl. Please check aidl is present at %1$s
|
||||
s_Removed_Recreating_s=%1$s was removed\! Recreating %1$s\!
|
||||
s_Modified_Manually_Recreating_s=%1$s was modified manually\! Reverting to generated version\!
|
||||
s_Modified_Recreating_s='%1$s' was modified, %2$s needs to be updated.
|
||||
Added_s_s_Needs_Updating=New resource file: '%1$s', %2$s needs to be updated.
|
||||
s_Removed_s_Needs_Updating='%1$s' was removed, %2$s needs to be updated.
|
||||
Requires_Compiler_Compliance_5=Android requires compiler compliance level 5.0. Please fix project properties.
|
||||
Requires_Source_Compatibility_5=Android requires source compatibility set to 5.0. Please fix project properties.
|
||||
Requires_Class_Compatibility_5=Android requires .class compatibility set to 5.0. Please fix project properties.
|
||||
Refreshing_Res=Refreshing resource folders.
|
||||
DexWrapper_s_does_not_exists=%1$s does not exist or is not a file
|
||||
DexWrapper_Failed_to_load_s=Failed to load %1$s
|
||||
DexWrapper_Unable_To_Execute_Dex_s=Unable to execute dex: %1$s
|
||||
DexWrapper_SecuryEx_Unable_To_Find_API=SecurityException: Unable to find API for dex.jar
|
||||
DexWrapper_SecuryEx_Unable_To_Find_Method=SecurityException: Unable to find method for dex.jar
|
||||
DexWrapper_SecuryEx_Unable_To_Find_Field=SecurityException: Unable to find field for dex.jar
|
||||
ApkBuilder_UnableBuild_Dex_Not_loaded=Unable to build: the file dex.jar was not loaded from the SDK folder\!
|
||||
ApkBuilder_Using_Default_Key=Using default debug key to sign package
|
||||
ApkBuilder_Using_s_To_Sign=Using '%1$s' to sign package
|
||||
ApkBuilder_Signing_Key_Creation_s=Signing Key Creation:
|
||||
ApkBuilder_Unable_To_Gey_Key=Unable to get debug signature key
|
||||
ApkBuilder_Certificate_Expired_on_s=Debug certificate expired on %1$s\!
|
||||
ApkBuilder_Packaging_s=Packaging %1$s
|
||||
ApkBuilder_JAVA_HOME_is_s=The Java VM Home used is: %1$s
|
||||
ApkBuilder_Update_or_Execute_manually_s=Update it if necessary, or manually execute the following command:
|
||||
ApkBuilder_s_Conflict_with_file_s=%1$s conflicts with another file already put at %2$s
|
||||
ApkBuilder_Packaging_s_into_s=Packaging %1$s into %2$s
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.launching;
|
||||
|
||||
import org.eclipse.debug.core.DebugException;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchManager;
|
||||
import org.eclipse.debug.core.Launch;
|
||||
import org.eclipse.debug.core.model.ISourceLocator;
|
||||
|
||||
/**
|
||||
* Custom implementation of Launch to allow access to the LaunchManager
|
||||
*
|
||||
*/
|
||||
class AndroidLaunch extends Launch {
|
||||
|
||||
/**
|
||||
* Basic constructor does nothing special
|
||||
* @param launchConfiguration
|
||||
* @param mode
|
||||
* @param locator
|
||||
*/
|
||||
public AndroidLaunch(ILaunchConfiguration launchConfiguration, String mode,
|
||||
ISourceLocator locator) {
|
||||
super(launchConfiguration, mode, locator);
|
||||
}
|
||||
|
||||
/** Stops the launch, and removes it from the launch manager */
|
||||
public void stopLaunch() {
|
||||
ILaunchManager mgr = getLaunchManager();
|
||||
|
||||
if (canTerminate()) {
|
||||
try {
|
||||
terminate();
|
||||
} catch (DebugException e) {
|
||||
// well looks like we couldn't stop it. nothing else to be
|
||||
// done really
|
||||
}
|
||||
}
|
||||
// remove the launch
|
||||
mgr.removeLaunch(this);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* 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.launching;
|
||||
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
import com.android.ddmlib.Client;
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
|
||||
import com.android.ddmlib.Device.DeviceState;
|
||||
import com.android.ddmuilib.IImageLoader;
|
||||
import com.android.ddmuilib.ImageHelper;
|
||||
import com.android.ddmuilib.TableHelper;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.AndroidLaunchConfiguration;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.DelayedLaunchInfo;
|
||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
import org.eclipse.jface.viewers.ILabelProviderListener;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredContentProvider;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.viewers.ITableLabelProvider;
|
||||
import org.eclipse.jface.viewers.StructuredSelection;
|
||||
import org.eclipse.jface.viewers.TableViewer;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.SWTException;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
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.Dialog;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.swt.widgets.Table;
|
||||
|
||||
public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener {
|
||||
|
||||
private final static int DLG_WIDTH = 500;
|
||||
private final static int DLG_HEIGHT = 300;
|
||||
private final static int ICON_WIDTH = 16;
|
||||
|
||||
private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
|
||||
private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
|
||||
private final static String PREFS_COL_BUILD = "deviceChooser.build"; //$NON-NLS-1$
|
||||
|
||||
private Table mDeviceTable;
|
||||
private TableViewer mViewer;
|
||||
|
||||
private Image mDeviceImage;
|
||||
private Image mEmulatorImage;
|
||||
|
||||
private Button mOkButton;
|
||||
private Button mCreateButton;
|
||||
|
||||
private DeviceChooserResponse mResponse;
|
||||
private DelayedLaunchInfo mLaunchInfo;
|
||||
|
||||
/**
|
||||
* Basic Content Provider for a table full of {@link Device} objects. The input is
|
||||
* a {@link AndroidDebugBridge}.
|
||||
*/
|
||||
private class ContentProvider implements IStructuredContentProvider {
|
||||
public Object[] getElements(Object inputElement) {
|
||||
if (inputElement instanceof AndroidDebugBridge) {
|
||||
return ((AndroidDebugBridge)inputElement).getDevices();
|
||||
}
|
||||
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A Label Provider for the {@link TableViewer} in {@link DeviceChooserDialog}.
|
||||
* It provides labels and images for {@link Device} objects.
|
||||
*/
|
||||
private class LabelProvider implements ITableLabelProvider {
|
||||
|
||||
public Image getColumnImage(Object element, int columnIndex) {
|
||||
if (columnIndex == 0 && element instanceof Device) {
|
||||
if (((Device)element).isEmulator()) {
|
||||
return mEmulatorImage;
|
||||
}
|
||||
|
||||
return mDeviceImage;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getColumnText(Object element, int columnIndex) {
|
||||
if (element instanceof Device) {
|
||||
Device device = (Device)element;
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return device.getSerialNumber();
|
||||
case 1:
|
||||
return getStateString(device);
|
||||
case 2:
|
||||
String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
|
||||
String version = device.getProperty(Device.PROP_BUILD_VERSION);
|
||||
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
|
||||
return String.format("%1$s (debug)", version); //$NON-NLS-1$
|
||||
} else {
|
||||
return String.format("%1$s", version); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addListener(ILabelProviderListener listener) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public boolean isLabelProperty(Object element, String property) {
|
||||
// pass
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeListener(ILabelProviderListener listener) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeviceChooserResponse {
|
||||
public boolean mustContinue;
|
||||
public boolean mustLaunchEmulator;
|
||||
public Device deviceToUse;
|
||||
}
|
||||
|
||||
public DeviceChooserDialog(Shell parent) {
|
||||
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and display the dialog.
|
||||
* @param response
|
||||
* @param project
|
||||
* @param launch
|
||||
* @param launchInfo
|
||||
* @param config
|
||||
*/
|
||||
public void open(DeviceChooserResponse response, IProject project,
|
||||
AndroidLaunch launch, DelayedLaunchInfo launchInfo, AndroidLaunchConfiguration config) {
|
||||
mResponse = response;
|
||||
mLaunchInfo = launchInfo;
|
||||
|
||||
Shell parent = getParent();
|
||||
Shell shell = new Shell(parent, getStyle());
|
||||
shell.setText("Device Chooser");
|
||||
|
||||
loadImages();
|
||||
createContents(shell);
|
||||
|
||||
// Set the dialog size.
|
||||
shell.setMinimumSize(DLG_WIDTH, DLG_HEIGHT);
|
||||
Rectangle r = parent.getBounds();
|
||||
// get the center new top left.
|
||||
int cx = r.x + r.width/2;
|
||||
int x = cx - DLG_WIDTH / 2;
|
||||
int cy = r.y + r.height/2;
|
||||
int y = cy - DLG_HEIGHT / 2;
|
||||
shell.setBounds(x, y, DLG_WIDTH, DLG_HEIGHT);
|
||||
|
||||
shell.pack();
|
||||
shell.open();
|
||||
|
||||
// start the listening.
|
||||
AndroidDebugBridge.addDeviceChangeListener(this);
|
||||
|
||||
Display display = parent.getDisplay();
|
||||
while (!shell.isDisposed()) {
|
||||
if (!display.readAndDispatch())
|
||||
display.sleep();
|
||||
}
|
||||
|
||||
// done listening.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(this);
|
||||
|
||||
mEmulatorImage.dispose();
|
||||
mDeviceImage.dispose();
|
||||
|
||||
AndroidLaunchController.getInstance().continueLaunch(response, project, launch,
|
||||
launchInfo, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the device chooser dialog contents.
|
||||
* @param shell the parent shell.
|
||||
*/
|
||||
private void createContents(final Shell shell) {
|
||||
shell.setLayout(new GridLayout(1, true));
|
||||
|
||||
shell.addListener(SWT.Close, new Listener() {
|
||||
public void handleEvent(Event event) {
|
||||
event.doit = true;
|
||||
}
|
||||
});
|
||||
|
||||
Label l = new Label(shell, SWT.NONE);
|
||||
l.setText("Select the target device.");
|
||||
|
||||
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
|
||||
mDeviceTable = new Table(shell, SWT.SINGLE | SWT.FULL_SELECTION);
|
||||
mDeviceTable.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
mDeviceTable.setHeaderVisible(true);
|
||||
mDeviceTable.setLinesVisible(true);
|
||||
|
||||
TableHelper.createTableColumn(mDeviceTable, "Serial Number",
|
||||
SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
|
||||
PREFS_COL_SERIAL, store);
|
||||
|
||||
TableHelper.createTableColumn(mDeviceTable, "State",
|
||||
SWT.LEFT, "bootloader", //$NON-NLS-1$
|
||||
PREFS_COL_STATE, store);
|
||||
|
||||
TableHelper.createTableColumn(mDeviceTable, "Build Info",
|
||||
SWT.LEFT, "engineering", //$NON-NLS-1$
|
||||
PREFS_COL_BUILD, store);
|
||||
|
||||
// create the viewer for it
|
||||
mViewer = new TableViewer(mDeviceTable);
|
||||
mViewer.setContentProvider(new ContentProvider());
|
||||
mViewer.setLabelProvider(new LabelProvider());
|
||||
mViewer.setInput(AndroidDebugBridge.getBridge());
|
||||
mViewer.addDoubleClickListener(new IDoubleClickListener() {
|
||||
public void doubleClick(DoubleClickEvent event) {
|
||||
ISelection selection = event.getSelection();
|
||||
if (selection instanceof IStructuredSelection) {
|
||||
IStructuredSelection structuredSelection = (IStructuredSelection)selection;
|
||||
Object object = structuredSelection.getFirstElement();
|
||||
if (object instanceof Device) {
|
||||
Device selectedDevice = (Device)object;
|
||||
|
||||
mResponse.deviceToUse = selectedDevice;
|
||||
mResponse.mustContinue = true;
|
||||
shell.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// bottom part with the ok/cancel
|
||||
Composite bottomComp = new Composite(shell, SWT.NONE);
|
||||
bottomComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
// 3 items in the layout: createButton, spacer, composite with ok/cancel
|
||||
// (to force same width).
|
||||
bottomComp.setLayout(new GridLayout(3 /* numColums */, false /* makeColumnsEqualWidth */));
|
||||
|
||||
mCreateButton = new Button(bottomComp, SWT.NONE);
|
||||
mCreateButton.setText("Launch Emulator");
|
||||
mCreateButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = true;
|
||||
mResponse.mustLaunchEmulator = true;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
|
||||
// the spacer
|
||||
Composite spacer = new Composite(bottomComp, SWT.NONE);
|
||||
GridData gd;
|
||||
spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.heightHint = 0;
|
||||
|
||||
// the composite to contain ok/cancel
|
||||
Composite buttonContainer = new Composite(bottomComp, SWT.NONE);
|
||||
GridLayout gl = new GridLayout(2 /* numColums */, true /* makeColumnsEqualWidth */);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
buttonContainer.setLayout(gl);
|
||||
|
||||
mOkButton = new Button(buttonContainer, SWT.NONE);
|
||||
mOkButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
mOkButton.setEnabled(false);
|
||||
mOkButton.setText("OK");
|
||||
mOkButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = true;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancelButton = new Button(buttonContainer, SWT.NONE);
|
||||
cancelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = false;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
|
||||
mDeviceTable.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
int count = mDeviceTable.getSelectionCount();
|
||||
if (count != 1) {
|
||||
handleSelection(null);
|
||||
} else {
|
||||
int index = mDeviceTable.getSelectionIndex();
|
||||
Object data = mViewer.getElementAt(index);
|
||||
if (data instanceof Device) {
|
||||
handleSelection((Device)data);
|
||||
} else {
|
||||
handleSelection(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mDeviceTable.setFocus();
|
||||
shell.setDefaultButton(mOkButton);
|
||||
|
||||
updateDefaultSelection();
|
||||
}
|
||||
|
||||
private void loadImages() {
|
||||
IImageLoader loader = DdmsPlugin.getImageLoader();
|
||||
Display display = DdmsPlugin.getDisplay();
|
||||
|
||||
if (mDeviceImage == null) {
|
||||
mDeviceImage = ImageHelper.loadImage(loader, display,
|
||||
"device.png", //$NON-NLS-1$
|
||||
ICON_WIDTH, ICON_WIDTH,
|
||||
display.getSystemColor(SWT.COLOR_RED));
|
||||
}
|
||||
if (mEmulatorImage == null) {
|
||||
mEmulatorImage = ImageHelper.loadImage(loader, display,
|
||||
"emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
|
||||
display.getSystemColor(SWT.COLOR_BLUE));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a display string representing the state of the device.
|
||||
* @param d the device
|
||||
*/
|
||||
private static String getStateString(Device d) {
|
||||
DeviceState deviceState = d.getState();
|
||||
if (deviceState == DeviceState.ONLINE) {
|
||||
return "Online";
|
||||
} else if (deviceState == DeviceState.OFFLINE) {
|
||||
return "Offline";
|
||||
} else if (deviceState == DeviceState.BOOTLOADER) {
|
||||
return "Bootloader";
|
||||
}
|
||||
|
||||
return "??";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent when the a device is connected to the {@link AndroidDebugBridge}.
|
||||
* <p/>
|
||||
* This is sent from a non UI thread.
|
||||
* @param device the new device.
|
||||
*
|
||||
* @see IDeviceChangeListener#deviceConnected(Device)
|
||||
*/
|
||||
public void deviceConnected(Device device) {
|
||||
final DeviceChooserDialog dialog = this;
|
||||
exec(new Runnable() {
|
||||
public void run() {
|
||||
if (mDeviceTable.isDisposed() == false) {
|
||||
// refresh all
|
||||
mViewer.refresh();
|
||||
|
||||
// update the selection
|
||||
updateDefaultSelection();
|
||||
} else {
|
||||
// table is disposed, we need to do something.
|
||||
// lets remove ourselves from the listener.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(dialog);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent when the a device is connected to the {@link AndroidDebugBridge}.
|
||||
* <p/>
|
||||
* This is sent from a non UI thread.
|
||||
* @param device the new device.
|
||||
*
|
||||
* @see IDeviceChangeListener#deviceDisconnected(Device)
|
||||
*/
|
||||
public void deviceDisconnected(Device device) {
|
||||
deviceConnected(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent when a device data changed, or when clients are started/terminated on the device.
|
||||
* <p/>
|
||||
* This is sent from a non UI thread.
|
||||
* @param device the device that was updated.
|
||||
* @param changeMask the mask indicating what changed.
|
||||
*
|
||||
* @see IDeviceChangeListener#deviceChanged(Device)
|
||||
*/
|
||||
public void deviceChanged(final Device device, int changeMask) {
|
||||
if ((changeMask & (Device.CHANGE_STATE | Device.CHANGE_BUILD_INFO)) != 0) {
|
||||
final DeviceChooserDialog dialog = this;
|
||||
exec(new Runnable() {
|
||||
public void run() {
|
||||
if (mDeviceTable.isDisposed() == false) {
|
||||
// refresh the device
|
||||
mViewer.refresh(device);
|
||||
|
||||
// update the defaultSelection.
|
||||
updateDefaultSelection();
|
||||
|
||||
// if the changed device is the current selection,
|
||||
// we update the OK button based on its state.
|
||||
if (device == mResponse.deviceToUse) {
|
||||
mOkButton.setEnabled(mResponse.deviceToUse.isOnline());
|
||||
}
|
||||
} else {
|
||||
// table is disposed, we need to do something.
|
||||
// lets remove ourselves from the listener.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(dialog);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the {@link Runnable} in the UI thread.
|
||||
* @param runnable the runnable to execute.
|
||||
*/
|
||||
private void exec(Runnable runnable) {
|
||||
try {
|
||||
Display display = mDeviceTable.getDisplay();
|
||||
display.asyncExec(runnable);
|
||||
} catch (SWTException e) {
|
||||
// tree is disposed, we need to do something. lets remove ourselves from the listener.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelection(Device device) {
|
||||
mResponse.deviceToUse = device;
|
||||
mOkButton.setEnabled(device != null && mResponse.deviceToUse.isOnline());
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a default device to select. This is done by looking for the running
|
||||
* clients on each device and finding one similar to the one being launched.
|
||||
* <p/>
|
||||
* This is done every time the device list changed, until there is a selection..
|
||||
*/
|
||||
private void updateDefaultSelection() {
|
||||
if (mDeviceTable.getSelectionCount() == 0) {
|
||||
AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
|
||||
|
||||
Device[] devices = bridge.getDevices();
|
||||
|
||||
for (Device device : devices) {
|
||||
Client[] clients = device.getClients();
|
||||
|
||||
for (Client client : clients) {
|
||||
|
||||
if (mLaunchInfo.mPackageName.equals(
|
||||
client.getClientData().getClientDescription())) {
|
||||
// found a match! Select it.
|
||||
mViewer.setSelection(new StructuredSelection(device));
|
||||
handleSelection(device);
|
||||
|
||||
// and we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* 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.launching;
|
||||
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.AndroidLaunchConfiguration;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
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.Status;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
|
||||
/**
|
||||
* Implementation of an eclipse LauncConfigurationDelegate to launch android
|
||||
* application in debug.
|
||||
*/
|
||||
public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
|
||||
final static int INVALID_DEBUG_PORT = -1;
|
||||
|
||||
public final static String ANDROID_LAUNCH_TYPE_ID =
|
||||
"com.android.ide.eclipse.adt.debug.LaunchConfigType"; //$NON-NLS-1$
|
||||
|
||||
/** Target mode parameters: true is automatic, false is manual */
|
||||
public static final String ATTR_TARGET_MODE = AdtPlugin.PLUGIN_ID + ".target"; //$NON-NLS-1$
|
||||
public static final boolean DEFAULT_TARGET_MODE = true; //automatic mode
|
||||
|
||||
/**
|
||||
* Launch action:
|
||||
* <ul>
|
||||
* <li>0: launch default activity</li>
|
||||
* <li>1: launch specified activity. See {@link #ATTR_ACTIVITY}</li>
|
||||
* <li>2: Do Nothing</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final static String ATTR_LAUNCH_ACTION = AdtPlugin.PLUGIN_ID + ".action"; //$NON-NLS-1$
|
||||
|
||||
/** Default launch action. This launches the activity that is setup to be found in the HOME
|
||||
* screen.
|
||||
*/
|
||||
public final static int ACTION_DEFAULT = 0;
|
||||
/** Launch action starting a specific activity. */
|
||||
public final static int ACTION_ACTIVITY = 1;
|
||||
/** Launch action that does nothing. */
|
||||
public final static int ACTION_DO_NOTHING = 2;
|
||||
/** Default launch action value. */
|
||||
public final static int DEFAULT_LAUNCH_ACTION = ACTION_DEFAULT;
|
||||
|
||||
/**
|
||||
* Activity to be launched if {@link #ATTR_LAUNCH_ACTION} is 1
|
||||
*/
|
||||
public static final String ATTR_ACTIVITY = AdtPlugin.PLUGIN_ID + ".activity"; //$NON-NLS-1$
|
||||
|
||||
/** 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$
|
||||
|
||||
/**
|
||||
* Index of the default network speed setting for the emulator.<br>
|
||||
* Get the emulator option with <code>EmulatorConfigTab.getSpeed(index)</code>
|
||||
*/
|
||||
public static final int DEFAULT_SPEED = 0;
|
||||
|
||||
public static final String ATTR_DELAY = AdtPlugin.PLUGIN_ID + ".delay"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Index of the default network latency setting for the emulator.<br>
|
||||
* Get the emulator option with <code>EmulatorConfigTab.getDelay(index)</code>
|
||||
*/
|
||||
public static final int DEFAULT_DELAY = 0;
|
||||
|
||||
public static final String ATTR_COMMANDLINE = AdtPlugin.PLUGIN_ID + ".commandline"; //$NON-NLS-1$
|
||||
|
||||
public static final String ATTR_WIPE_DATA = AdtPlugin.PLUGIN_ID + ".wipedata"; //$NON-NLS-1$
|
||||
public static final boolean DEFAULT_WIPE_DATA = false;
|
||||
|
||||
public static final String ATTR_NO_BOOT_ANIM = AdtPlugin.PLUGIN_ID + ".nobootanim"; //$NON-NLS-1$
|
||||
public static final boolean DEFAULT_NO_BOOT_ANIM = false;
|
||||
|
||||
public static final String ATTR_DEBUG_PORT =
|
||||
AdtPlugin.PLUGIN_ID + ".debugPort"; //$NON-NLS-1$
|
||||
|
||||
public void launch(ILaunchConfiguration configuration, String mode,
|
||||
ILaunch launch, IProgressMonitor monitor) throws CoreException {
|
||||
// We need to check if it's a standard launch or if it's a launch
|
||||
// to debug an application already running.
|
||||
int debugPort = AndroidLaunchController.getPortForConfig(configuration);
|
||||
|
||||
// get the project
|
||||
IProject project = getProject(configuration);
|
||||
|
||||
// first we make sure the launch is of the proper type
|
||||
AndroidLaunch androidLaunch = null;
|
||||
if (launch instanceof AndroidLaunch) {
|
||||
androidLaunch = (AndroidLaunch)launch;
|
||||
} else {
|
||||
// wrong type, not sure how we got there, but we don't do
|
||||
// anything else
|
||||
AdtPlugin.printErrorToConsole(project, "Wrong Launch Type!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (project == null) {
|
||||
AdtPlugin.printErrorToConsole("Couldn't get project object!");
|
||||
androidLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the project has errors, and abort in this case.
|
||||
if (ProjectHelper.hasError(project, true)) {
|
||||
AdtPlugin.displayError("Android Launch",
|
||||
"Your project contains error(s), please fix them before running your application.");
|
||||
return;
|
||||
}
|
||||
|
||||
AdtPlugin.printToConsole(project, "------------------------------"); //$NON-NLS-1$
|
||||
AdtPlugin.printToConsole(project, "Android Launch!");
|
||||
|
||||
// check if the project is using the proper sdk.
|
||||
// if that throws an exception, we simply let it propage to the caller.
|
||||
if (checkAndroidProject(project) == false) {
|
||||
AdtPlugin.printErrorToConsole(project, "Project is not an Android Project. Aborting!");
|
||||
androidLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check adb status and abort if needed.
|
||||
AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
|
||||
if (bridge == null || bridge.isConnected() == false) {
|
||||
try {
|
||||
int connections = -1;
|
||||
int restarts = -1;
|
||||
if (bridge != null) {
|
||||
connections = bridge.getConnectionAttemptCount();
|
||||
restarts = bridge.getRestartAttemptCount();
|
||||
}
|
||||
|
||||
// if we get -1, the device monitor is not even setup (anymore?).
|
||||
// We need to ask the user to restart eclipse.
|
||||
// This shouldn't happen, but it's better to let the user know in case it does.
|
||||
if (connections == -1 || restarts == -1) {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"The connection to adb is down, and a severe error has occured.",
|
||||
"You must restart adb and Eclipse.",
|
||||
String.format(
|
||||
"Please ensure that adb is correctly located at '%1$s' and can be executed.",
|
||||
AdtPlugin.getOsAbsoluteAdb()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (restarts == 0) {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"Connection with adb was interrupted.",
|
||||
String.format("%1$s attempts have been made to reconnect.", connections),
|
||||
"You may want to manually restart adb from the Devices view.");
|
||||
} else {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"Connection with adb was interrupted, and attempts to reconnect have failed.",
|
||||
String.format("%1$s attempts have been made to restart adb.", restarts),
|
||||
"You may want to manually restart adb from the Devices view.");
|
||||
|
||||
}
|
||||
return;
|
||||
} finally {
|
||||
androidLaunch.stopLaunch();
|
||||
}
|
||||
}
|
||||
|
||||
// since adb is working, we let the user know
|
||||
// TODO have a verbose mode for launch with more info (or some of the less useful info we now have).
|
||||
AdtPlugin.printToConsole(project, "adb is running normally.");
|
||||
|
||||
// make a config class
|
||||
AndroidLaunchConfiguration config = new AndroidLaunchConfiguration();
|
||||
|
||||
// fill it with the config coming from the ILaunchConfiguration object
|
||||
config.set(configuration);
|
||||
|
||||
// get the launch controller singleton
|
||||
AndroidLaunchController controller = AndroidLaunchController.getInstance();
|
||||
|
||||
// get the application package
|
||||
IFile applicationPackage = getApplicationPackage(project);
|
||||
if (applicationPackage == null) {
|
||||
androidLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
// we need some information from the manifest
|
||||
AndroidManifestParser manifestParser = AndroidManifestParser.parse(
|
||||
BaseProjectHelper.getJavaProject(project), null /* errorListener */,
|
||||
true /* gatherData */, false /* markErrors */);
|
||||
|
||||
if (manifestParser == null) {
|
||||
AdtPlugin.printErrorToConsole(project, "Failed to parse AndroidManifest: aborting!");
|
||||
androidLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
String activityName = null;
|
||||
|
||||
if (config.mLaunchAction == ACTION_ACTIVITY) {
|
||||
// Get the activity name defined in the config
|
||||
activityName = getActivityName(configuration);
|
||||
|
||||
// Get the full activity list and make sure the one we got matches.
|
||||
String[] activities = manifestParser.getActivities();
|
||||
|
||||
// first we check that there are, in fact, activities.
|
||||
if (activities.length == 0) {
|
||||
// if the activities list is null, then the manifest is empty
|
||||
// and we can't launch the app. We'll revert to a sync-only launch
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"The Manifest defines no activity!",
|
||||
"The launch will only sync the application package on the device!");
|
||||
config.mLaunchAction = ACTION_DO_NOTHING;
|
||||
} else if (activityName == null) {
|
||||
// if the activity we got is null, we look for the default one.
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"No activity specified! Getting the launcher activity.");
|
||||
activityName = manifestParser.getLauncherActivity();
|
||||
|
||||
// if there's no default activity. We revert to a sync-only launch.
|
||||
if (activityName == null) {
|
||||
revertToNoActionLaunch(project, config);
|
||||
}
|
||||
} else {
|
||||
|
||||
// check the one we got from the config matches any from the list
|
||||
boolean match = false;
|
||||
for (String a : activities) {
|
||||
if (a != null && a.equals(activityName)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a match, we revert to the default activity if any.
|
||||
if (match == false) {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"The specified activity does not exist! Getting the launcher activity.");
|
||||
activityName = manifestParser.getLauncherActivity();
|
||||
|
||||
// if there's no default activity. We revert to a sync-only launch.
|
||||
if (activityName == null) {
|
||||
revertToNoActionLaunch(project, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (config.mLaunchAction == ACTION_DEFAULT) {
|
||||
activityName = manifestParser.getLauncherActivity();
|
||||
|
||||
// if there's no default activity. We revert to a sync-only launch.
|
||||
if (activityName == null) {
|
||||
revertToNoActionLaunch(project, config);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean buildForLaunch(ILaunchConfiguration configuration,
|
||||
String mode, IProgressMonitor monitor) throws CoreException {
|
||||
|
||||
// need to check we have everything
|
||||
IProject project = getProject(configuration);
|
||||
|
||||
if (project != null) {
|
||||
// force an incremental build to be sure the resources will
|
||||
// be updated if they were not saved before the launch was launched.
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
|
||||
1 /* code, unused */, "Can't find the project!", null /* exception */));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
|
||||
throws CoreException {
|
||||
return new AndroidLaunch(configuration, mode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IProject object matching the name found in the configuration
|
||||
* object under the name
|
||||
* <code>IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME</code>
|
||||
* @param configuration
|
||||
* @return The IProject object or null
|
||||
*/
|
||||
private IProject getProject(ILaunchConfiguration configuration){
|
||||
// get the project name from the config
|
||||
String projectName;
|
||||
try {
|
||||
projectName = configuration.getAttribute(
|
||||
IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, "");
|
||||
} catch (CoreException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the current workspace
|
||||
IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||
|
||||
// and return the project with the name from the config
|
||||
return workspace.getRoot().getProject(projectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the project is an android project.
|
||||
* @param project The project to check
|
||||
* @return true if the project is an android SDK.
|
||||
* @throws CoreException
|
||||
*/
|
||||
private boolean checkAndroidProject(IProject project) throws CoreException {
|
||||
// check if the project is a java and an android project.
|
||||
if (project.hasNature(JavaCore.NATURE_ID) == false) {
|
||||
String msg = String.format("%1$s is not a Java project!", project.getName());
|
||||
AdtPlugin.displayError("Android Launch", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (project.hasNature(AndroidConstants.NATURE) == false) {
|
||||
String msg = String.format("%1$s is not an Android project!", project.getName());
|
||||
AdtPlugin.displayError("Android Launch", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the android package file as an IFile object for the specified
|
||||
* project.
|
||||
* @param project The project
|
||||
* @return The android package as an IFile object or null if not found.
|
||||
*/
|
||||
private IFile getApplicationPackage(IProject project) {
|
||||
// get the output folder
|
||||
IFolder outputLocation = BaseProjectHelper.getOutputFolder(project);
|
||||
|
||||
if (outputLocation == null) {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"Failed to get the output location of the project. Check build path properties"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// get the package path
|
||||
String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
|
||||
IResource r = outputLocation.findMember(packageName);
|
||||
|
||||
// check the package is present
|
||||
if (r instanceof IFile && r.exists()) {
|
||||
return (IFile)r;
|
||||
}
|
||||
|
||||
String msg = String.format("Could not find %1$s!", packageName);
|
||||
AdtPlugin.printErrorToConsole(project, msg);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the activity.
|
||||
* @param configuration
|
||||
* @return
|
||||
*/
|
||||
private String getActivityName(ILaunchConfiguration configuration) {
|
||||
String empty = "";
|
||||
String activityName;
|
||||
try {
|
||||
activityName = configuration.getAttribute(ATTR_ACTIVITY, empty);
|
||||
} catch (CoreException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (activityName != empty) ? activityName : null;
|
||||
}
|
||||
|
||||
private final void revertToNoActionLaunch(IProject project, AndroidLaunchConfiguration config) {
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
"No Launcher activity found!",
|
||||
"The launch will only sync the application package on the device!");
|
||||
config.mLaunchAction = ACTION_DO_NOTHING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.eclipse.org/org/documents/epl-v10.php
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.debug.launching;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.ui.DebugUITools;
|
||||
import org.eclipse.debug.ui.ILaunchShortcut;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
|
||||
/**
|
||||
* Launch shortcut to launch debug/run configuration directly.
|
||||
*/
|
||||
public class LaunchShortcut implements ILaunchShortcut {
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchShortcut#launch(
|
||||
* org.eclipse.jface.viewers.ISelection, java.lang.String)
|
||||
*/
|
||||
public void launch(ISelection selection, String mode) {
|
||||
if (selection instanceof IStructuredSelection) {
|
||||
|
||||
// get the object and the project from it
|
||||
IStructuredSelection structSelect = (IStructuredSelection)selection;
|
||||
Object o = structSelect.getFirstElement();
|
||||
|
||||
// get the first (and normally only) element
|
||||
if (o instanceof IAdaptable) {
|
||||
IResource r = (IResource)((IAdaptable)o).getAdapter(IResource.class);
|
||||
|
||||
// get the project from the resource
|
||||
if (r != null) {
|
||||
IProject project = r.getProject();
|
||||
|
||||
if (project != null) {
|
||||
// and launch
|
||||
launch(project, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchShortcut#launch(
|
||||
* org.eclipse.ui.IEditorPart, java.lang.String)
|
||||
*/
|
||||
public void launch(IEditorPart editor, String mode) {
|
||||
// since we force the shortcut to only work on selection in the
|
||||
// package explorer, this will never be called.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Launch a config for the specified project.
|
||||
* @param project The project to launch
|
||||
* @param mode The launch mode ("debug", "run" or "profile")
|
||||
*/
|
||||
private void launch(IProject project, String mode) {
|
||||
// get an existing or new launch configuration
|
||||
ILaunchConfiguration config = AndroidLaunchController.getLaunchConfig(project);
|
||||
|
||||
if (config != null) {
|
||||
// and launch!
|
||||
DebugUITools.launch(config, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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.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.ddms.DdmsPlugin;
|
||||
|
||||
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.jface.preference.IPreferenceStore;
|
||||
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.graphics.Font;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
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.Group;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
/**
|
||||
* Launch configuration tab to control the parameters of the Emulator
|
||||
*/
|
||||
public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
|
||||
private final static String[][] NETWORK_SPEEDS = new String[][] {
|
||||
{ "Full", "full" }, //$NON-NLS-2$
|
||||
{ "GSM", "gsm" }, //$NON-NLS-2$
|
||||
{ "HSCSD", "hscsd" }, //$NON-NLS-2$
|
||||
{ "GPRS", "gprs" }, //$NON-NLS-2$
|
||||
{ "EDGE", "edge" }, //$NON-NLS-2$
|
||||
{ "UMTS", "umts" }, //$NON-NLS-2$
|
||||
{ "HSPDA", "hsdpa" }, //$NON-NLS-2$
|
||||
};
|
||||
|
||||
private final static String[][] NETWORK_LATENCIES = new String[][] {
|
||||
{ "None", "none" }, //$NON-NLS-2$
|
||||
{ "GPRS", "gprs" }, //$NON-NLS-2$
|
||||
{ "EDGE", "edge" }, //$NON-NLS-2$
|
||||
{ "UMTS", "umts" }, //$NON-NLS-2$
|
||||
};
|
||||
|
||||
private Combo mSpeedCombo;
|
||||
|
||||
private Combo mDelayCombo;
|
||||
|
||||
private Group mEmulatorOptionsGroup;
|
||||
|
||||
private Text mEmulatorCLOptions;
|
||||
|
||||
private Combo mSkinCombo;
|
||||
|
||||
private Button mAutoTargetButton;
|
||||
|
||||
private Button mManualTargetButton;
|
||||
|
||||
private Button mWipeDataButton;
|
||||
|
||||
private Button mNoBootAnimButton;
|
||||
|
||||
/**
|
||||
* Returns the emulator ready speed option value.
|
||||
* @param value The index of the combo selection.
|
||||
*/
|
||||
public static String getSpeed(int value) {
|
||||
try {
|
||||
return NETWORK_SPEEDS[value][1];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
return NETWORK_SPEEDS[LaunchConfigDelegate.DEFAULT_SPEED][1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the emulator ready network latency value.
|
||||
* @param value The index of the combo selection.
|
||||
*/
|
||||
public static String getDelay(int value) {
|
||||
try {
|
||||
return NETWORK_LATENCIES[value][1];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
return NETWORK_LATENCIES[LaunchConfigDelegate.DEFAULT_DELAY][1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public EmulatorConfigTab() {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite)
|
||||
*/
|
||||
public void createControl(Composite parent) {
|
||||
Font font = parent.getFont();
|
||||
|
||||
Composite topComp = new Composite(parent, SWT.NONE);
|
||||
setControl(topComp);
|
||||
GridLayout topLayout = new GridLayout();
|
||||
topLayout.numColumns = 1;
|
||||
topLayout.verticalSpacing = 0;
|
||||
topComp.setLayout(topLayout);
|
||||
topComp.setFont(font);
|
||||
|
||||
GridData gd;
|
||||
GridLayout layout;
|
||||
|
||||
// radio button for the target mode
|
||||
Group targetModeGroup = new Group(topComp, SWT.NONE);
|
||||
targetModeGroup.setText("Device Target Selection Mode");
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
targetModeGroup.setLayoutData(gd);
|
||||
layout = new GridLayout();
|
||||
layout.numColumns = 1;
|
||||
targetModeGroup.setLayout(layout);
|
||||
targetModeGroup.setFont(font);
|
||||
|
||||
// add the radio button
|
||||
mAutoTargetButton = new Button(targetModeGroup, SWT.RADIO);
|
||||
mAutoTargetButton.setText("Automatic");
|
||||
mAutoTargetButton.setSelection(true);
|
||||
mAutoTargetButton.addSelectionListener(new SelectionAdapter() {
|
||||
// called when selection changes
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
|
||||
mManualTargetButton = new Button(targetModeGroup, SWT.RADIO);
|
||||
mManualTargetButton.setText("Manual");
|
||||
// Since there are only 2 radio buttons, we can put a listener on only
|
||||
// one (they
|
||||
// are both called on select and unselect event.
|
||||
|
||||
// emulator size
|
||||
mEmulatorOptionsGroup = new Group(topComp, SWT.NONE);
|
||||
mEmulatorOptionsGroup.setText("Emulator launch parameters:");
|
||||
mEmulatorOptionsGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
layout = new GridLayout();
|
||||
layout.numColumns = 2;
|
||||
mEmulatorOptionsGroup.setLayout(layout);
|
||||
mEmulatorOptionsGroup.setFont(font);
|
||||
|
||||
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
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
mSkinCombo.pack();
|
||||
|
||||
// network options
|
||||
new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Network Speed:");
|
||||
|
||||
mSpeedCombo = new Combo(mEmulatorOptionsGroup, SWT.READ_ONLY);
|
||||
for (String[] speed : NETWORK_SPEEDS) {
|
||||
mSpeedCombo.add(speed[0]);
|
||||
}
|
||||
mSpeedCombo.addSelectionListener(new SelectionAdapter() {
|
||||
// called when selection changes
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
mSpeedCombo.pack();
|
||||
|
||||
new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Network Latency:");
|
||||
|
||||
mDelayCombo = new Combo(mEmulatorOptionsGroup, SWT.READ_ONLY);
|
||||
|
||||
for (String[] delay : NETWORK_LATENCIES) {
|
||||
mDelayCombo.add(delay[0]);
|
||||
}
|
||||
mDelayCombo.addSelectionListener(new SelectionAdapter() {
|
||||
// called when selection changes
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
mDelayCombo.pack();
|
||||
|
||||
// wipe data option
|
||||
mWipeDataButton = new Button(mEmulatorOptionsGroup, SWT.CHECK);
|
||||
mWipeDataButton.setText("Wipe User Data");
|
||||
mWipeDataButton.setToolTipText("Check this if you want to wipe your user data each time you start the emulator. You will be prompted for confirmation when the emulator starts.");
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
mWipeDataButton.setLayoutData(gd);
|
||||
mWipeDataButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
|
||||
// no boot anim option
|
||||
mNoBootAnimButton = new Button(mEmulatorOptionsGroup, SWT.CHECK);
|
||||
mNoBootAnimButton.setText("Disable Boot Animation");
|
||||
mNoBootAnimButton.setToolTipText("Check this if you want to disable the boot animation. This can help the emulator start faster on slow machines.");
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
mNoBootAnimButton.setLayoutData(gd);
|
||||
mNoBootAnimButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
|
||||
// custom command line option for emulator
|
||||
Label l = new Label(mEmulatorOptionsGroup, SWT.NONE);
|
||||
l.setText("Additional Emulator Command Line Options");
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
l.setLayoutData(gd);
|
||||
|
||||
mEmulatorCLOptions = new Text(mEmulatorOptionsGroup, SWT.BORDER);
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
mEmulatorCLOptions.setLayoutData(gd);
|
||||
mEmulatorCLOptions.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
|
||||
*/
|
||||
public String getName() {
|
||||
return "Target";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage() {
|
||||
return DdmsPlugin.getImageLoader().loadImage("emulator.png", null); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void initializeFrom(ILaunchConfiguration configuration) {
|
||||
boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true ==
|
||||
// automatic
|
||||
try {
|
||||
value = configuration.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, value);
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll use the default value
|
||||
}
|
||||
mAutoTargetButton.setSelection(value);
|
||||
mManualTargetButton.setSelection(!value);
|
||||
|
||||
value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
|
||||
try {
|
||||
value = configuration.getAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, value);
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll use the default value
|
||||
}
|
||||
mWipeDataButton.setSelection(value);
|
||||
|
||||
value = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM;
|
||||
try {
|
||||
value = configuration.getAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM, value);
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll use the default value
|
||||
}
|
||||
mNoBootAnimButton.setSelection(value);
|
||||
|
||||
int index = -1;
|
||||
try {
|
||||
String skin = configuration.getAttribute(LaunchConfigDelegate.ATTR_SKIN, (String)null);
|
||||
if (skin != null) {
|
||||
index = getSkinIndex(skin);
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
index = getSkinIndex(SkinRepository.getInstance().checkSkin(
|
||||
LaunchConfigDelegate.DEFAULT_SKIN));
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
mSkinCombo.clearSelection();
|
||||
} else {
|
||||
mSkinCombo.select(index);
|
||||
}
|
||||
|
||||
index = LaunchConfigDelegate.DEFAULT_SPEED;
|
||||
try {
|
||||
index = configuration.getAttribute(LaunchConfigDelegate.ATTR_SPEED,
|
||||
index);
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll use the default value
|
||||
}
|
||||
if (index == -1) {
|
||||
mSpeedCombo.clearSelection();
|
||||
} else {
|
||||
mSpeedCombo.select(index);
|
||||
}
|
||||
|
||||
index = LaunchConfigDelegate.DEFAULT_DELAY;
|
||||
try {
|
||||
index = configuration.getAttribute(LaunchConfigDelegate.ATTR_DELAY,
|
||||
index);
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll put a proper value in
|
||||
// performApply anyway
|
||||
}
|
||||
if (index == -1) {
|
||||
mDelayCombo.clearSelection();
|
||||
} else {
|
||||
mDelayCombo.select(index);
|
||||
}
|
||||
|
||||
String commandLine = null;
|
||||
try {
|
||||
commandLine = configuration.getAttribute(
|
||||
LaunchConfigDelegate.ATTR_COMMANDLINE, ""); //$NON-NLS-1$
|
||||
} catch (CoreException e) {
|
||||
// let's not do anything here, we'll use the default value
|
||||
}
|
||||
if (commandLine != null) {
|
||||
mEmulatorCLOptions.setText(commandLine);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
|
||||
*/
|
||||
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
|
||||
mAutoTargetButton.getSelection());
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN,
|
||||
getSkinNameByIndex(mSkinCombo.getSelectionIndex()));
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
|
||||
mSpeedCombo.getSelectionIndex());
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
|
||||
mDelayCombo.getSelectionIndex());
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_COMMANDLINE,
|
||||
mEmulatorCLOptions.getText());
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA,
|
||||
mWipeDataButton.getSelection());
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM,
|
||||
mNoBootAnimButton.getSelection());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
|
||||
*/
|
||||
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
|
||||
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN,
|
||||
LaunchConfigDelegate.DEFAULT_SKIN);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
|
||||
LaunchConfigDelegate.DEFAULT_SPEED);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
|
||||
LaunchConfigDelegate.DEFAULT_DELAY);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA,
|
||||
LaunchConfigDelegate.DEFAULT_WIPE_DATA);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM,
|
||||
LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM);
|
||||
|
||||
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
|
||||
String emuOptions = store.getString(AdtPlugin.PREFS_EMU_OPTIONS);
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_COMMANDLINE, emuOptions);
|
||||
}
|
||||
|
||||
private String getSkinNameByIndex(int index) {
|
||||
return SkinRepository.getInstance().getSkinNameByIndex(index);
|
||||
}
|
||||
|
||||
private int getSkinIndex(String name) {
|
||||
return SkinRepository.getInstance().getSkinIndex(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
|
||||
import org.eclipse.debug.ui.CommonTab;
|
||||
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
|
||||
import org.eclipse.debug.ui.ILaunchConfigurationTab;
|
||||
|
||||
/**
|
||||
* Tab group object for Android Launch Config type.
|
||||
*/
|
||||
public class LaunchConfigTabGroup extends AbstractLaunchConfigurationTabGroup {
|
||||
|
||||
public LaunchConfigTabGroup() {
|
||||
}
|
||||
|
||||
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
|
||||
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
|
||||
new MainLaunchConfigTab(),
|
||||
new EmulatorConfigTab(),
|
||||
new CommonTab()
|
||||
};
|
||||
setTabs(tabs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* 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.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController;
|
||||
import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
|
||||
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
|
||||
import org.eclipse.debug.ui.ILaunchConfigurationTab;
|
||||
import org.eclipse.jdt.core.IJavaModel;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
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.events.SelectionListener;
|
||||
import org.eclipse.swt.graphics.Font;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
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.Group;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
/**
|
||||
* Class for the main launch configuration tab.
|
||||
*/
|
||||
public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
|
||||
|
||||
protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
|
||||
|
||||
protected Text mProjText;
|
||||
private Button mProjButton;
|
||||
|
||||
private Combo mActivityCombo;
|
||||
private String[] mActivities;
|
||||
|
||||
private WidgetListener mListener = new WidgetListener();
|
||||
|
||||
private Button mDefaultActionButton;
|
||||
private Button mActivityActionButton;
|
||||
private Button mDoNothingActionButton;
|
||||
private int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
|
||||
|
||||
private ProjectChooserHelper mProjectChooserHelper;
|
||||
|
||||
/**
|
||||
* A listener which handles widget change events for the controls in this
|
||||
* tab.
|
||||
*/
|
||||
private class WidgetListener implements ModifyListener, SelectionListener {
|
||||
|
||||
public void modifyText(ModifyEvent e) {
|
||||
IProject project = checkParameters();
|
||||
loadActivities(project);
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
public void widgetDefaultSelected(SelectionEvent e) {/* do nothing */
|
||||
}
|
||||
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
Object source = e.getSource();
|
||||
if (source == mProjButton) {
|
||||
handleProjectButtonSelected();
|
||||
} else {
|
||||
checkParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MainLaunchConfigTab() {
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
mProjectChooserHelper = new ProjectChooserHelper(parent.getShell());
|
||||
|
||||
Font font = parent.getFont();
|
||||
Composite comp = new Composite(parent, SWT.NONE);
|
||||
setControl(comp);
|
||||
GridLayout topLayout = new GridLayout();
|
||||
topLayout.verticalSpacing = 0;
|
||||
comp.setLayout(topLayout);
|
||||
comp.setFont(font);
|
||||
createProjectEditor(comp);
|
||||
createVerticalSpacer(comp, 1);
|
||||
|
||||
// create the combo for the activity chooser
|
||||
Group group = new Group(comp, SWT.NONE);
|
||||
group.setText("Launch Action:");
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
group.setLayoutData(gd);
|
||||
GridLayout layout = new GridLayout();
|
||||
layout.numColumns = 2;
|
||||
group.setLayout(layout);
|
||||
group.setFont(font);
|
||||
|
||||
mDefaultActionButton = new Button(group, SWT.RADIO);
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
mDefaultActionButton.setLayoutData(gd);
|
||||
mDefaultActionButton.setText("Launch Default Activity");
|
||||
mDefaultActionButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
// event are received for both selection and deselection, so we only process
|
||||
// the selection event to avoid doing it twice.
|
||||
if (mDefaultActionButton.getSelection() == true) {
|
||||
mLaunchAction = LaunchConfigDelegate.ACTION_DEFAULT;
|
||||
mActivityCombo.setEnabled(false);
|
||||
checkParameters();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mActivityActionButton = new Button(group, SWT.RADIO);
|
||||
mActivityActionButton.setText("Launch:");
|
||||
mActivityActionButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
// event are received for both selection and deselection, so we only process
|
||||
// the selection event to avoid doing it twice.
|
||||
if (mActivityActionButton.getSelection() == true) {
|
||||
mLaunchAction = LaunchConfigDelegate.ACTION_ACTIVITY;
|
||||
mActivityCombo.setEnabled(true);
|
||||
checkParameters();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mActivityCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY);
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
mActivityCombo.setLayoutData(gd);
|
||||
mActivityCombo.clearSelection();
|
||||
mActivityCombo.setEnabled(false);
|
||||
mActivityCombo.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
checkParameters();
|
||||
}
|
||||
});
|
||||
|
||||
mDoNothingActionButton = new Button(group, SWT.RADIO);
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
mDoNothingActionButton.setLayoutData(gd);
|
||||
mDoNothingActionButton.setText("Do Nothing");
|
||||
mDoNothingActionButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
// event are received for both selection and deselection, so we only process
|
||||
// the selection event to avoid doing it twice.
|
||||
if (mDoNothingActionButton.getSelection() == true) {
|
||||
mLaunchAction = LaunchConfigDelegate.ACTION_DO_NOTHING;
|
||||
mActivityCombo.setEnabled(false);
|
||||
checkParameters();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Android";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage() {
|
||||
return AdtPlugin.getImageLoader().loadImage("mainLaunchTab.png", null);
|
||||
}
|
||||
|
||||
|
||||
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
|
||||
configuration.setAttribute(
|
||||
IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, mProjText.getText());
|
||||
configuration.setAttribute(
|
||||
IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true);
|
||||
|
||||
// add the launch mode
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, mLaunchAction);
|
||||
|
||||
// add the activity
|
||||
int selection = mActivityCombo.getSelectionIndex();
|
||||
if (mActivities != null && selection >=0 && selection < mActivities.length) {
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, mActivities[selection]);
|
||||
}
|
||||
|
||||
// link the project and the launch config.
|
||||
mapResources(configuration);
|
||||
}
|
||||
|
||||
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
|
||||
configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
|
||||
LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the widgets for specifying a main type.
|
||||
*
|
||||
* @param parent the parent composite
|
||||
*/
|
||||
protected void createProjectEditor(Composite parent) {
|
||||
Font font = parent.getFont();
|
||||
Group group = new Group(parent, SWT.NONE);
|
||||
group.setText("Project:");
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
group.setLayoutData(gd);
|
||||
GridLayout layout = new GridLayout();
|
||||
layout.numColumns = 2;
|
||||
group.setLayout(layout);
|
||||
group.setFont(font);
|
||||
mProjText = new Text(group, SWT.SINGLE | SWT.BORDER);
|
||||
gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
mProjText.setLayoutData(gd);
|
||||
mProjText.setFont(font);
|
||||
mProjText.addModifyListener(mListener);
|
||||
mProjButton = createPushButton(group, "Browse...", null);
|
||||
mProjButton.addSelectionListener(mListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the default listener from this class. For all subclasses this
|
||||
* listener will only provide the functi Jaonality of updating the current
|
||||
* tab
|
||||
*
|
||||
* @return a widget listener
|
||||
*/
|
||||
protected WidgetListener getDefaultListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link IJavaProject} corresponding to the project name in the project
|
||||
* name text field, or null if the text does not match a project name.
|
||||
* @param javaModel the Java Model object corresponding for the current workspace root.
|
||||
* @return a IJavaProject object or null.
|
||||
*/
|
||||
protected IJavaProject getJavaProject(IJavaModel javaModel) {
|
||||
String projectName = mProjText.getText().trim();
|
||||
if (projectName.length() < 1) {
|
||||
return null;
|
||||
}
|
||||
return javaModel.getJavaProject(projectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog that lets the user select a project. This in turn provides
|
||||
* context for the main type, allowing the user to key a main type name, or
|
||||
* constraining the search for main types to the specified project.
|
||||
*/
|
||||
protected void handleProjectButtonSelected() {
|
||||
IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject(
|
||||
mProjText.getText().trim());
|
||||
if (javaProject == null) {
|
||||
return;
|
||||
}// end if
|
||||
String projectName = javaProject.getElementName();
|
||||
mProjText.setText(projectName);
|
||||
|
||||
// get the list of activities and fill the combo
|
||||
IProject project = javaProject.getProject();
|
||||
loadActivities(project);
|
||||
}// end handle selected
|
||||
|
||||
/**
|
||||
* Initializes this tab's controls with values from the given
|
||||
* launch configuration. This method is called when
|
||||
* a configuration is selected to view or edit, after this
|
||||
* tab's control has been created.
|
||||
*
|
||||
* @param config launch configuration
|
||||
*
|
||||
* @see ILaunchConfigurationTab
|
||||
*/
|
||||
public void initializeFrom(ILaunchConfiguration config) {
|
||||
String projectName = EMPTY_STRING;
|
||||
try {
|
||||
projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
|
||||
EMPTY_STRING);
|
||||
}// end try
|
||||
catch (CoreException ce) {
|
||||
}
|
||||
mProjText.setText(projectName);
|
||||
|
||||
// get the list of projects
|
||||
IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null);
|
||||
|
||||
if (projects != null) {
|
||||
// look for the currently selected project
|
||||
IProject proj = null;
|
||||
for (IJavaProject p : projects) {
|
||||
if (p.getElementName().equals(projectName)) {
|
||||
proj = p.getProject();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loadActivities(proj);
|
||||
}
|
||||
|
||||
// load the launch action.
|
||||
mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
|
||||
try {
|
||||
mLaunchAction = config.getAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
|
||||
mLaunchAction);
|
||||
} catch (CoreException e) {
|
||||
// nothing to be done really. launchAction will keep its default value.
|
||||
}
|
||||
|
||||
mDefaultActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_DEFAULT);
|
||||
mActivityActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY);
|
||||
mDoNothingActionButton.setSelection(
|
||||
mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING);
|
||||
|
||||
// now look for the activity and load it if present, otherwise, revert
|
||||
// to the current one.
|
||||
String activityName = EMPTY_STRING;
|
||||
try {
|
||||
activityName = config.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, EMPTY_STRING);
|
||||
}// end try
|
||||
catch (CoreException ce) {
|
||||
// nothing to be done really. activityName will stay empty
|
||||
}
|
||||
|
||||
if (mLaunchAction != LaunchConfigDelegate.ACTION_ACTIVITY) {
|
||||
mActivityCombo.setEnabled(false);
|
||||
mActivityCombo.clearSelection();
|
||||
} else {
|
||||
mActivityCombo.setEnabled(true);
|
||||
if (activityName == null || activityName.equals(EMPTY_STRING)) {
|
||||
mActivityCombo.clearSelection();
|
||||
} else if (mActivities != null && mActivities.length > 0) {
|
||||
// look for the name of the activity in the combo.
|
||||
boolean found = false;
|
||||
for (int i = 0 ; i < mActivities.length ; i++) {
|
||||
if (activityName.equals(mActivities[i])) {
|
||||
found = true;
|
||||
mActivityCombo.select(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't found a matching activity we clear the combo selection
|
||||
if (found == false) {
|
||||
mActivityCombo.clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the launch config and the project. This allows Eclipse to delete the launch
|
||||
* config when the project is deleted.
|
||||
*
|
||||
* @param config the launch config working copy.
|
||||
*/
|
||||
protected void mapResources(ILaunchConfigurationWorkingCopy config) {
|
||||
// get the java model
|
||||
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
|
||||
IJavaModel javaModel = JavaCore.create(workspaceRoot);
|
||||
|
||||
// get the IJavaProject described by the text field.
|
||||
IJavaProject javaProject = getJavaProject(javaModel);
|
||||
IResource[] resources = null;
|
||||
if (javaProject != null) {
|
||||
resources = AndroidLaunchController.getResourcesToMap(javaProject.getProject());
|
||||
}
|
||||
config.setMappedResources(resources);
|
||||
}
|
||||
|
||||
/** Loads the ui with the activities of the specified project, and store the
|
||||
* activities in <code>mActivities</code>
|
||||
* First activity is selected by default if present.
|
||||
* @param project The project to load the activities from
|
||||
* @return The array of activities or null if none could be found.
|
||||
*/
|
||||
private void loadActivities(IProject project) {
|
||||
if (project != null) {
|
||||
try {
|
||||
// parse the manifest for the list of activities.
|
||||
AndroidManifestParser manifestParser = AndroidManifestParser.parse(
|
||||
BaseProjectHelper.getJavaProject(project), null /* errorListener */,
|
||||
true /* gatherData */, false /* markErrors */);
|
||||
if (manifestParser != null) {
|
||||
mActivities = manifestParser.getActivities();
|
||||
|
||||
mActivityCombo.removeAll();
|
||||
|
||||
if (mActivities.length > 0) {
|
||||
if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) {
|
||||
mActivityCombo.setEnabled(true);
|
||||
}
|
||||
for (String s : mActivities) {
|
||||
mActivityCombo.add(s);
|
||||
}
|
||||
} else {
|
||||
mActivityCombo.setEnabled(false);
|
||||
}
|
||||
|
||||
// the selection will be set when we update the ui from the current
|
||||
// config object.
|
||||
mActivityCombo.clearSelection();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (CoreException e) {
|
||||
// The AndroidManifest parsing failed. The builders must have reported the errors
|
||||
// already so there's nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach this point, either project is null, or we got an exception during
|
||||
// the parsing. In either case, we empty the activity list.
|
||||
mActivityCombo.removeAll();
|
||||
mActivities = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the parameters for correctness, and update the error message and buttons.
|
||||
* @return the current IProject of this launch config.
|
||||
*/
|
||||
private IProject checkParameters() {
|
||||
try {
|
||||
//test the project name first!
|
||||
String text = mProjText.getText();
|
||||
if (text.length() == 0) {
|
||||
setErrorMessage("Project Name is required!");
|
||||
} else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) {
|
||||
setErrorMessage("Project name contains unsupported characters!");
|
||||
} else {
|
||||
IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null);
|
||||
IProject found = null;
|
||||
for (IJavaProject javaProject : projects) {
|
||||
if (javaProject.getProject().getName().equals(text)) {
|
||||
found = javaProject.getProject();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
setErrorMessage(null);
|
||||
} else {
|
||||
setErrorMessage(String.format("There is no android project named '%1$s'",
|
||||
text));
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
} finally {
|
||||
updateLaunchConfigurationDialog();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
Dialog_Title_SDK_Location=Android SDK Location
|
||||
SDK_Not_Setup=The location of the Android SDK has not been setup. Please go to Preferences > Android and set it up
|
||||
Error_Check_Prefs=%1$s\nPlease check the Android plugin's preferences.
|
||||
Could_Not_Find_Folder=Could not find SDK folder '%1$s'.
|
||||
Could_Not_Find_Folder_In_SDK=Could not find folder '%1$s' inside SDK '%2$s'.
|
||||
Could_Not_Find=Could not find %1$s\!
|
||||
VersionCheck_SDK_Milestone_Too_Low=This version of ADT requires a SDK in version M%1$d or above.\n\nCurrent version is %2$s.\n\nPlease update your SDK to the latest version.
|
||||
VersionCheck_SDK_Build_Too_Low=This version of ADT requires a SDK in version %1$d (M%2$d) or above.\n\nCurrent version is %3$s.\n\nPlease update your SDK to the latest version.
|
||||
VersionCheck_Plugin_Version_Failed=Failed to get the required ADT version number from the SDK.\n\nThe Android Developer Toolkit may not work properly.
|
||||
VersionCheck_Unable_To_Parse_Version_s=Unable to parse sdk build version: %1$s
|
||||
VersionCheck_Plugin_Too_Old=This Android SDK requires Android Developer Toolkit version %1$d.%2$d.%3$d or above.\n\nCurrent version is %4$s.\n\nPlease update ADT to the latest version.
|
||||
AdtPlugin_Failed_To_Start_s=Failed to start %1$s
|
||||
AdtPlugin_Android_SDK_Content_Loader=Android SDK Content Loader
|
||||
AdtPlugin_Parsing_Resources=Parsing Resources
|
||||
AdtPlugin_Android_SDK_Resource_Parser=Android SDK Resource Parser
|
||||
AdtPlugin_Failed_To_Parse_s=Failed to parse:
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.preferences;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
|
||||
import org.eclipse.jface.preference.DirectoryFieldEditor;
|
||||
import org.eclipse.jface.preference.FieldEditorPreferencePage;
|
||||
import org.eclipse.jface.resource.JFaceResources;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPreferencePage;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* This class represents a preference page that is contributed to the
|
||||
* Preferences dialog. By subclassing <samp>FieldEditorPreferencePage</samp>,
|
||||
* we can use the field support built into JFace that allows us to create a page
|
||||
* that is small and knows how to save, restore and apply itself.
|
||||
* <p>
|
||||
* This page is used to modify preferences only. They are stored in the
|
||||
* preference store that belongs to the main plug-in class. That way,
|
||||
* preferences can be accessed directly via the preference store.
|
||||
*/
|
||||
|
||||
public class AndroidPreferencePage extends FieldEditorPreferencePage implements
|
||||
IWorkbenchPreferencePage {
|
||||
|
||||
public AndroidPreferencePage() {
|
||||
super(GRID);
|
||||
setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore());
|
||||
setDescription(Messages.AndroidPreferencePage_Title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the field editors. Field editors are abstractions of the common
|
||||
* GUI blocks needed to manipulate various types of preferences. Each field
|
||||
* editor knows how to save and restore itself.
|
||||
*/
|
||||
@Override
|
||||
public void createFieldEditors() {
|
||||
|
||||
addField(new SdkDirectoryFieldEditor(AdtPlugin.PREFS_SDK_DIR,
|
||||
Messages.AndroidPreferencePage_SDK_Location_, getFieldEditorParent()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
|
||||
*/
|
||||
public void init(IWorkbench workbench) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom version of DirectoryFieldEditor which validates that the directory really
|
||||
* contains an SDK.
|
||||
*
|
||||
* There's a known issue here, which is really a rare edge-case: if the pref dialog is open
|
||||
* which a given sdk directory and the *content* of the directory changes such that the sdk
|
||||
* state changed (i.e. from valid to invalid or vice versa), the pref panel will display or
|
||||
* hide the error as appropriate but the pref panel will fail to validate the apply/ok buttons
|
||||
* appropriately. The easy workaround is to cancel the pref panel and enter it again.
|
||||
*/
|
||||
private static class SdkDirectoryFieldEditor extends DirectoryFieldEditor {
|
||||
|
||||
public SdkDirectoryFieldEditor(String name, String labelText, Composite parent) {
|
||||
super(name, labelText, parent);
|
||||
setEmptyStringAllowed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method declared on StringFieldEditor and overridden in DirectoryFieldEditor.
|
||||
* Checks whether the text input field contains a valid directory.
|
||||
*
|
||||
* @return True if the apply/ok button should be enabled in the pref panel
|
||||
*/
|
||||
@Override
|
||||
protected boolean doCheckState() {
|
||||
String fileName = getTextControl().getText();
|
||||
fileName = fileName.trim();
|
||||
|
||||
if (fileName.indexOf(',') >= 0 || fileName.indexOf(';') >= 0) {
|
||||
setErrorMessage(Messages.AndroidPreferencePage_ERROR_Reserved_Char);
|
||||
return false; // Apply/OK must be disabled
|
||||
}
|
||||
|
||||
File file = new File(fileName);
|
||||
if (!file.isDirectory()) {
|
||||
setErrorMessage(JFaceResources.getString(
|
||||
"DirectoryFieldEditor.errorMessage")); //$NON-NLS-1$
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(fileName,
|
||||
new AdtPlugin.CheckSdkErrorHandler() {
|
||||
@Override
|
||||
public boolean handleError(String message) {
|
||||
setErrorMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
return false; // Apply/OK must be disabled
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleWarning(String message) {
|
||||
showMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
return true; // Apply/OK must be enabled
|
||||
}
|
||||
});
|
||||
if (ok) clearMessage();
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getTextControl(Composite parent) {
|
||||
setValidateStrategy(VALIDATE_ON_KEY_STROKE);
|
||||
return super.getTextControl(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.preferences;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
|
||||
import org.eclipse.jface.preference.BooleanFieldEditor;
|
||||
import org.eclipse.jface.preference.FieldEditorPreferencePage;
|
||||
import org.eclipse.jface.preference.FileFieldEditor;
|
||||
import org.eclipse.jface.preference.RadioGroupFieldEditor;
|
||||
import org.eclipse.jface.preference.StringFieldEditor;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPreferencePage;
|
||||
|
||||
/**
|
||||
* Preference page for build options.
|
||||
*
|
||||
*/
|
||||
public class BuildPreferencePage extends FieldEditorPreferencePage implements
|
||||
IWorkbenchPreferencePage {
|
||||
|
||||
final static String BUILD_STR_SILENT = "silent"; //$NON-NLS-1$
|
||||
final static String BUILD_STR_NORMAL = "normal"; //$NON-NLS-1$
|
||||
final static String BUILD_STR_VERBOSE = "verbose"; //$NON-NLS-1$
|
||||
|
||||
public BuildPreferencePage() {
|
||||
super(GRID);
|
||||
setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore());
|
||||
setDescription(Messages.BuildPreferencePage_Title);
|
||||
}
|
||||
|
||||
public static int getBuildLevel(String buildPrefValue) {
|
||||
if (BUILD_STR_SILENT.equals(buildPrefValue)) {
|
||||
return AdtConstants.BUILD_ALWAYS;
|
||||
} else if (BUILD_STR_VERBOSE.equals(buildPrefValue)) {
|
||||
return AdtConstants.BUILD_VERBOSE;
|
||||
}
|
||||
|
||||
return AdtConstants.BUILD_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createFieldEditors() {
|
||||
addField(new BooleanFieldEditor(AdtPlugin.PREFS_RES_AUTO_REFRESH,
|
||||
Messages.BuildPreferencePage_Auto_Refresh_Resources_on_Build,
|
||||
getFieldEditorParent()));
|
||||
|
||||
RadioGroupFieldEditor rgfe = new RadioGroupFieldEditor(
|
||||
AdtPlugin.PREFS_BUILD_VERBOSITY,
|
||||
Messages.BuildPreferencePage_Build_Output, 1, new String[][] {
|
||||
{ Messages.BuildPreferencePage_Silent, BUILD_STR_SILENT },
|
||||
{ Messages.BuildPreferencePage_Normal, BUILD_STR_NORMAL },
|
||||
{ Messages.BuildPreferencePage_Verbose, BUILD_STR_VERBOSE }
|
||||
},
|
||||
getFieldEditorParent(), true);
|
||||
addField(rgfe);
|
||||
|
||||
addField(new ReadOnlyFieldEditor(AdtPlugin.PREFS_DEFAULT_DEBUG_KEYSTORE,
|
||||
Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent()));
|
||||
|
||||
addField(new FileFieldEditor(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE,
|
||||
Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent()));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
|
||||
*/
|
||||
public void init(IWorkbench workbench) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only string field editor.
|
||||
*/
|
||||
private static class ReadOnlyFieldEditor extends StringFieldEditor {
|
||||
|
||||
public ReadOnlyFieldEditor(String name, String labelText, Composite parent) {
|
||||
super(name, labelText, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createControl(Composite parent) {
|
||||
super.createControl(parent);
|
||||
|
||||
Text control = getTextControl();
|
||||
control.setEditable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.preferences;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
|
||||
import org.eclipse.jface.preference.FieldEditorPreferencePage;
|
||||
import org.eclipse.jface.preference.StringFieldEditor;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPreferencePage;
|
||||
|
||||
/**
|
||||
* Settings page for launch related preferences.
|
||||
*/
|
||||
public class LaunchPreferencePage extends FieldEditorPreferencePage implements
|
||||
IWorkbenchPreferencePage {
|
||||
|
||||
public LaunchPreferencePage() {
|
||||
super(GRID);
|
||||
setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore());
|
||||
setDescription(Messages.LaunchPreferencePage_Title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createFieldEditors() {
|
||||
addField(new StringFieldEditor(AdtPlugin.PREFS_EMU_OPTIONS,
|
||||
Messages.LaunchPreferencePage_Default_Emu_Options, getFieldEditorParent()));
|
||||
|
||||
addField(new StringFieldEditor(AdtPlugin.PREFS_HOME_PACKAGE,
|
||||
Messages.LaunchPreferencePage_Default_HOME_Package, getFieldEditorParent()));
|
||||
}
|
||||
|
||||
public void init(IWorkbench workbench) {
|
||||
// pass
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
package com.android.ide.eclipse.adt.preferences;
|
||||
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
public class Messages extends NLS {
|
||||
private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.preferences.messages"; //$NON-NLS-1$
|
||||
|
||||
public static String AndroidPreferencePage_ERROR_Reserved_Char;
|
||||
|
||||
public static String AndroidPreferencePage_SDK_Location_;
|
||||
|
||||
public static String AndroidPreferencePage_Title;
|
||||
|
||||
public static String BuildPreferencePage_Auto_Refresh_Resources_on_Build;
|
||||
|
||||
public static String BuildPreferencePage_Build_Output;
|
||||
|
||||
public static String BuildPreferencePage_Custom_Keystore;
|
||||
|
||||
public static String BuildPreferencePage_Default_KeyStore;
|
||||
|
||||
public static String BuildPreferencePage_Normal;
|
||||
|
||||
public static String BuildPreferencePage_Silent;
|
||||
|
||||
public static String BuildPreferencePage_Title;
|
||||
|
||||
public static String BuildPreferencePage_Verbose;
|
||||
|
||||
public static String LaunchPreferencePage_Default_Emu_Options;
|
||||
|
||||
public static String LaunchPreferencePage_Default_HOME_Package;
|
||||
|
||||
public static String LaunchPreferencePage_Title;
|
||||
static {
|
||||
// initialize resource bundle
|
||||
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
|
||||
}
|
||||
|
||||
private Messages() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.preferences;
|
||||
|
||||
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.core.runtime.preferences.AbstractPreferenceInitializer;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
|
||||
/**
|
||||
* Class used to initialize default preference values.
|
||||
*/
|
||||
public class PreferenceInitializer extends AbstractPreferenceInitializer {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer
|
||||
* #initializeDefaultPreferences()
|
||||
*/
|
||||
@Override
|
||||
public void initializeDefaultPreferences() {
|
||||
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
|
||||
|
||||
store.setDefault(AdtPlugin.PREFS_RES_AUTO_REFRESH, true);
|
||||
|
||||
store.setDefault(AdtPlugin.PREFS_BUILD_VERBOSITY, BuildPreferencePage.BUILD_STR_NORMAL);
|
||||
|
||||
store.setDefault(AdtPlugin.PREFS_HOME_PACKAGE, "android.process.acore"); //$NON-NLS-1$
|
||||
|
||||
try {
|
||||
store.setDefault(AdtPlugin.PREFS_DEFAULT_DEBUG_KEYSTORE,
|
||||
DebugKeyProvider.getDefaultKeyStoreOsPath());
|
||||
} catch (KeytoolException e) {
|
||||
AdtPlugin.log(e, "Get default debug keystore path failed"); //$NON-NLS-1$
|
||||
} catch (AndroidLocationException e) {
|
||||
AdtPlugin.log(e, "Get default debug keystore path failed"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
BuildPreferencePage_Title=Build Settings:
|
||||
BuildPreferencePage_Auto_Refresh_Resources_on_Build=Automatically refresh Resources and Assets folder on build
|
||||
BuildPreferencePage_Build_Output=Build output
|
||||
BuildPreferencePage_Silent=Silent
|
||||
BuildPreferencePage_Normal=Normal
|
||||
BuildPreferencePage_Verbose=Verbose
|
||||
BuildPreferencePage_Default_KeyStore=Default debug keystore:
|
||||
BuildPreferencePage_Custom_Keystore=Custom debug keystore:
|
||||
LaunchPreferencePage_Title=Launch Settings:
|
||||
LaunchPreferencePage_Default_Emu_Options=Default emulator options:
|
||||
LaunchPreferencePage_Default_HOME_Package=Default HOME package:
|
||||
AndroidPreferencePage_Title=Android Preferences
|
||||
AndroidPreferencePage_SDK_Location_=SDK Location:
|
||||
AndroidPreferencePage_ERROR_Reserved_Char=Reserved characters ',' and ';' cannot be used in the SDK Location.
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.build.ApkBuilder;
|
||||
import com.android.ide.eclipse.adt.build.PreCompilerBuilder;
|
||||
import com.android.ide.eclipse.adt.build.ResourceManagerBuilder;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.resources.ICommand;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IProjectNature;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
|
||||
/**
|
||||
* Project nature for the Android Projects.
|
||||
*/
|
||||
public class AndroidNature implements IProjectNature {
|
||||
|
||||
/** the project this nature object is associated with */
|
||||
private IProject mProject;
|
||||
|
||||
/**
|
||||
* Configures this nature for its project. This is called by the workspace
|
||||
* when natures are added to the project using
|
||||
* <code>IProject.setDescription</code> and should not be called directly
|
||||
* by clients. The nature extension id is added to the list of natures
|
||||
* before this method is called, and need not be added here.
|
||||
*
|
||||
* Exceptions thrown by this method will be propagated back to the caller of
|
||||
* <code>IProject.setDescription</code>, but the nature will remain in
|
||||
* the project description.
|
||||
*
|
||||
* The Android nature adds the pre-builder and the APK builder if necessary.
|
||||
*
|
||||
* @see org.eclipse.core.resources.IProjectNature#configure()
|
||||
* @throws CoreException if configuration fails.
|
||||
*/
|
||||
public void configure() throws CoreException {
|
||||
configureResourceManagerBuilder(mProject);
|
||||
configurePreBuilder(mProject);
|
||||
configureApkBuilder(mProject);
|
||||
}
|
||||
|
||||
/**
|
||||
* De-configures this nature for its project. This is called by the
|
||||
* workspace when natures are removed from the project using
|
||||
* <code>IProject.setDescription</code> and should not be called directly
|
||||
* by clients. The nature extension id is removed from the list of natures
|
||||
* before this method is called, and need not be removed here.
|
||||
*
|
||||
* Exceptions thrown by this method will be propagated back to the caller of
|
||||
* <code>IProject.setDescription</code>, but the nature will still be
|
||||
* removed from the project description.
|
||||
*
|
||||
* The Android nature removes the custom pre builder and APK builder.
|
||||
*
|
||||
* @see org.eclipse.core.resources.IProjectNature#deconfigure()
|
||||
* @throws CoreException if configuration fails.
|
||||
*/
|
||||
public void deconfigure() throws CoreException {
|
||||
// remove the android builders
|
||||
removeBuilder(mProject, ResourceManagerBuilder.ID);
|
||||
removeBuilder(mProject, PreCompilerBuilder.ID);
|
||||
removeBuilder(mProject, ApkBuilder.ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the project to which this project nature applies.
|
||||
*
|
||||
* @return the project handle
|
||||
* @see org.eclipse.core.resources.IProjectNature#getProject()
|
||||
*/
|
||||
public IProject getProject() {
|
||||
return mProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the project to which this nature applies. Used when instantiating
|
||||
* this project nature runtime. This is called by
|
||||
* <code>IProject.create()</code> or
|
||||
* <code>IProject.setDescription()</code> and should not be called
|
||||
* directly by clients.
|
||||
*
|
||||
* @param project the project to which this nature applies
|
||||
* @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject)
|
||||
*/
|
||||
public void setProject(IProject project) {
|
||||
mProject = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Android Nature and the Java Nature to the project if it doesn't
|
||||
* already have them.
|
||||
*
|
||||
* @param project An existing or new project to update
|
||||
* @param monitor An optional progress monitor. Can be null.
|
||||
* @throws CoreException if fails to change the nature.
|
||||
*/
|
||||
public static synchronized void setupProjectNatures(IProject project,
|
||||
IProgressMonitor monitor) throws CoreException {
|
||||
if (project == null || !project.isOpen()) return;
|
||||
if (monitor == null) monitor = new NullProgressMonitor();
|
||||
|
||||
// Add the natures. We need to add the Java nature first, so it adds its builder to the
|
||||
// project first. This way, when the android nature is added, we can control where to put
|
||||
// the android builders in relation to the java builder.
|
||||
// Adding the java nature after the android one, would place the java builder before the
|
||||
// android builders.
|
||||
addNatureToProjectDescription(project, JavaCore.NATURE_ID, monitor);
|
||||
addNatureToProjectDescription(project, AndroidConstants.NATURE, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified nature to the specified project. The nature is only
|
||||
* added if not already present.
|
||||
* <p/>
|
||||
* Android Natures are always inserted at the beginning of the list of natures in order to
|
||||
* have the jdt views/dialogs display the proper icon.
|
||||
*
|
||||
* @param project The project to modify.
|
||||
* @param natureId The Id of the nature to add.
|
||||
* @param monitor An existing progress monitor.
|
||||
* @throws CoreException if fails to change the nature.
|
||||
*/
|
||||
private static void addNatureToProjectDescription(IProject project,
|
||||
String natureId, IProgressMonitor monitor) throws CoreException {
|
||||
if (!project.hasNature(natureId)) {
|
||||
|
||||
IProjectDescription description = project.getDescription();
|
||||
String[] natures = description.getNatureIds();
|
||||
String[] newNatures = new String[natures.length + 1];
|
||||
|
||||
// Android natures always come first.
|
||||
if (natureId.equals(AndroidConstants.NATURE)) {
|
||||
System.arraycopy(natures, 0, newNatures, 1, natures.length);
|
||||
newNatures[0] = natureId;
|
||||
} else {
|
||||
System.arraycopy(natures, 0, newNatures, 0, natures.length);
|
||||
newNatures[natures.length] = natureId;
|
||||
}
|
||||
|
||||
description.setNatureIds(newNatures);
|
||||
project.setDescription(description, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the ResourceManagerBuilder, if its not already there. It'll insert
|
||||
* itself as the first builder.
|
||||
* @throws CoreException
|
||||
*
|
||||
*/
|
||||
public static void configureResourceManagerBuilder(IProject project)
|
||||
throws CoreException {
|
||||
// get the builder list
|
||||
IProjectDescription desc = project.getDescription();
|
||||
ICommand[] commands = desc.getBuildSpec();
|
||||
|
||||
// look for the builder in case it's already there.
|
||||
for (int i = 0; i < commands.length; ++i) {
|
||||
if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// it's not there, lets add it at the beginning of the builders
|
||||
ICommand[] newCommands = new ICommand[commands.length + 1];
|
||||
System.arraycopy(commands, 0, newCommands, 1, commands.length);
|
||||
ICommand command = desc.newCommand();
|
||||
command.setBuilderName(ResourceManagerBuilder.ID);
|
||||
newCommands[0] = command;
|
||||
desc.setBuildSpec(newCommands);
|
||||
project.setDescription(desc, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the PreCompilerBuilder if its not already there. It'll check for
|
||||
* presence of the ResourceManager and insert itself right after.
|
||||
* @param project
|
||||
* @throws CoreException
|
||||
*/
|
||||
public static void configurePreBuilder(IProject project)
|
||||
throws CoreException {
|
||||
// get the builder list
|
||||
IProjectDescription desc = project.getDescription();
|
||||
ICommand[] commands = desc.getBuildSpec();
|
||||
|
||||
// look for the builder in case it's already there.
|
||||
for (int i = 0; i < commands.length; ++i) {
|
||||
if (PreCompilerBuilder.ID.equals(commands[i].getBuilderName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we need to add it after the resource manager builder.
|
||||
// Let's look for it
|
||||
int index = -1;
|
||||
for (int i = 0; i < commands.length; ++i) {
|
||||
if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we're inserting after
|
||||
index++;
|
||||
|
||||
// do the insertion
|
||||
|
||||
// copy the builders before.
|
||||
ICommand[] newCommands = new ICommand[commands.length + 1];
|
||||
System.arraycopy(commands, 0, newCommands, 0, index);
|
||||
|
||||
// insert the new builder
|
||||
ICommand command = desc.newCommand();
|
||||
command.setBuilderName(PreCompilerBuilder.ID);
|
||||
newCommands[index] = command;
|
||||
|
||||
// copy the builder after
|
||||
System.arraycopy(commands, index, newCommands, index + 1, commands.length-index);
|
||||
|
||||
// set the new builders in the project
|
||||
desc.setBuildSpec(newCommands);
|
||||
project.setDescription(desc, null);
|
||||
}
|
||||
|
||||
public static void configureApkBuilder(IProject project)
|
||||
throws CoreException {
|
||||
// Add the .apk builder at the end if it's not already there
|
||||
IProjectDescription desc = project.getDescription();
|
||||
ICommand[] commands = desc.getBuildSpec();
|
||||
|
||||
for (int i = 0; i < commands.length; ++i) {
|
||||
if (ApkBuilder.ID.equals(commands[i].getBuilderName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ICommand[] newCommands = new ICommand[commands.length + 1];
|
||||
System.arraycopy(commands, 0, newCommands, 0, commands.length);
|
||||
ICommand command = desc.newCommand();
|
||||
command.setBuilderName(ApkBuilder.ID);
|
||||
newCommands[commands.length] = command;
|
||||
desc.setBuildSpec(newCommands);
|
||||
project.setDescription(desc, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a builder from the project.
|
||||
* @param project The project to remove the builder from.
|
||||
* @param id The String ID of the builder to remove.
|
||||
* @return true if the builder was found and removed.
|
||||
* @throws CoreException
|
||||
*/
|
||||
public static boolean removeBuilder(IProject project, String id) throws CoreException {
|
||||
IProjectDescription description = project.getDescription();
|
||||
ICommand[] commands = description.getBuildSpec();
|
||||
for (int i = 0; i < commands.length; ++i) {
|
||||
if (id.equals(commands[i].getBuilderName())) {
|
||||
ICommand[] newCommands = new ICommand[commands.length - 1];
|
||||
System.arraycopy(commands, 0, newCommands, 0, i);
|
||||
System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
|
||||
description.setBuildSpec(newCommands);
|
||||
project.setDescription(description, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.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.IProjectDescription;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.ui.IObjectActionDelegate;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Converts a project created with the activity creator into an
|
||||
* Android project.
|
||||
*/
|
||||
public class ConvertToAndroidAction implements IObjectActionDelegate {
|
||||
|
||||
private ISelection mSelection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
|
||||
*/
|
||||
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see IActionDelegate#run(IAction)
|
||||
*/
|
||||
public void run(IAction action) {
|
||||
if (mSelection instanceof IStructuredSelection) {
|
||||
for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
|
||||
Object element = it.next();
|
||||
IProject project = null;
|
||||
if (element instanceof IProject) {
|
||||
project = (IProject)element;
|
||||
} else if (element instanceof IAdaptable) {
|
||||
project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
|
||||
}
|
||||
if (project != null) {
|
||||
convertProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see IActionDelegate#selectionChanged(IAction, ISelection)
|
||||
*/
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
this.mSelection = selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles sample nature on a project
|
||||
*
|
||||
* @param project to have sample nature added or removed
|
||||
*/
|
||||
private void convertProject(final IProject project) {
|
||||
new Job("Convert Project") {
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
if (monitor != null) {
|
||||
monitor.beginTask(String.format(
|
||||
"Convert %1$s to Android", project.getName()), 5);
|
||||
}
|
||||
|
||||
IProjectDescription description = project.getDescription();
|
||||
String[] natures = description.getNatureIds();
|
||||
|
||||
// check if the project already has the android nature.
|
||||
for (int i = 0; i < natures.length; ++i) {
|
||||
if (AndroidConstants.NATURE.equals(natures[i])) {
|
||||
// we shouldn't be here as the visibility of the item
|
||||
// is dependent on the project.
|
||||
return new Status(Status.WARNING, AdtPlugin.PLUGIN_ID,
|
||||
"Project is already an Android project");
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
String[] newNatures = new String[natures.length + 1];
|
||||
System.arraycopy(natures, 0, newNatures, 1, natures.length);
|
||||
newNatures[0] = AndroidConstants.NATURE;
|
||||
|
||||
// set the new nature list in the project
|
||||
description.setNatureIds(newNatures);
|
||||
project.setDescription(description, null);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
// Fix the classpath entries.
|
||||
// get a java project
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
ProjectHelper.fixProjectClasspathEntries(javaProject);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
} catch (JavaModelException e) {
|
||||
return e.getJavaModelStatus();
|
||||
} catch (CoreException e) {
|
||||
return e.getStatus();
|
||||
} finally {
|
||||
if (monitor != null) {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IPackageFragment;
|
||||
import org.eclipse.jdt.core.IPackageFragmentRoot;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.ITypeHierarchy;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.ui.IObjectActionDelegate;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Action going through all the source of a project and creating a pre-processed aidl file
|
||||
* with all the custom parcelable classes.
|
||||
*/
|
||||
public class CreateAidlImportAction implements IObjectActionDelegate {
|
||||
|
||||
private ISelection mSelection;
|
||||
|
||||
public CreateAidlImportAction() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
|
||||
*/
|
||||
public void run(IAction action) {
|
||||
if (mSelection instanceof IStructuredSelection) {
|
||||
for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
|
||||
Object element = it.next();
|
||||
IProject project = null;
|
||||
if (element instanceof IProject) {
|
||||
project = (IProject)element;
|
||||
} else if (element instanceof IAdaptable) {
|
||||
project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
|
||||
}
|
||||
if (project != null) {
|
||||
final IProject fproject = project;
|
||||
new Job("Aidl preprocess") {
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
return createImportFile(fproject, monitor);
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
mSelection = selection;
|
||||
}
|
||||
|
||||
private IStatus createImportFile(IProject project, IProgressMonitor monitor) {
|
||||
try {
|
||||
if (monitor != null) {
|
||||
monitor.beginTask(String.format(
|
||||
"Creating aid preprocess file for %1$s", project.getName()), 1);
|
||||
}
|
||||
|
||||
ArrayList<String> parcelables = new ArrayList<String>();
|
||||
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
|
||||
|
||||
for (IPackageFragmentRoot root : roots) {
|
||||
if (root.isArchive() == false && root.isExternal() == false) {
|
||||
parsePackageFragmentRoot(root, parcelables, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
// create the file with the parcelables
|
||||
if (parcelables.size() > 0) {
|
||||
IPath path = project.getLocation();
|
||||
path = path.append("/project.aidl"); //$NON-NLS-1$
|
||||
|
||||
File f = new File(path.toOSString());
|
||||
if (f.exists() == false) {
|
||||
if (f.createNewFile() == false) {
|
||||
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
|
||||
"Failed to create /project.aidl");
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter fw = new FileWriter(f);
|
||||
|
||||
fw.write("// This file is auto-generated by the\n");
|
||||
fw.write("// 'Create Aidl preprocess file for Parcelable classes'\n");
|
||||
fw.write("// action. Do not modify!\n\n");
|
||||
|
||||
for (String parcelable : parcelables) {
|
||||
fw.write("parcelable "); //$NON-NLS-1$
|
||||
fw.write(parcelable);
|
||||
fw.append(";\n"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
fw.close();
|
||||
|
||||
// need to refresh the level just below the project to make sure it's being picked
|
||||
// up by eclipse.
|
||||
project.refreshLocal(IResource.DEPTH_ONE, monitor);
|
||||
}
|
||||
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
monitor.done();
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
} catch (JavaModelException e) {
|
||||
return e.getJavaModelStatus();
|
||||
} catch (IOException e) {
|
||||
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
|
||||
"Failed to create /project.aidl", e);
|
||||
} catch (CoreException e) {
|
||||
return e.getStatus();
|
||||
} finally {
|
||||
if (monitor != null) {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePackageFragmentRoot(IPackageFragmentRoot root,
|
||||
ArrayList<String> parcelables, IProgressMonitor monitor) throws JavaModelException {
|
||||
|
||||
IJavaElement[] elements = root.getChildren();
|
||||
|
||||
for (IJavaElement element : elements) {
|
||||
if (element instanceof IPackageFragment) {
|
||||
ICompilationUnit[] compilationUnits =
|
||||
((IPackageFragment)element).getCompilationUnits();
|
||||
|
||||
for (ICompilationUnit unit : compilationUnits) {
|
||||
IType[] types = unit.getTypes();
|
||||
|
||||
for (IType type : types) {
|
||||
parseType(type, parcelables, monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseType(IType type, ArrayList<String> parcelables, IProgressMonitor monitor)
|
||||
throws JavaModelException {
|
||||
// first look in this type if it somehow extends parcelable.
|
||||
ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor);
|
||||
|
||||
IType[] superInterfaces = typeHierarchy.getAllSuperInterfaces(type);
|
||||
for (IType superInterface : superInterfaces) {
|
||||
if ("android.os.Parcelable".equals(superInterface.getFullyQualifiedName())) { //$NON-NLS-1$
|
||||
parcelables.add(type.getFullyQualifiedName());
|
||||
}
|
||||
}
|
||||
|
||||
// then look in inner types.
|
||||
IType[] innerTypes = type.getTypes();
|
||||
|
||||
for (IType innerType : innerTypes) {
|
||||
parseType(innerType, parcelables, monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.common.project.ExportHelper;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.ui.IObjectActionDelegate;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
|
||||
public class ExportAction implements IObjectActionDelegate {
|
||||
|
||||
private ISelection mSelection;
|
||||
|
||||
/**
|
||||
* @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
|
||||
*/
|
||||
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
||||
}
|
||||
|
||||
public void run(IAction action) {
|
||||
if (mSelection instanceof IStructuredSelection) {
|
||||
IStructuredSelection selection = (IStructuredSelection)mSelection;
|
||||
// get the unique selected item.
|
||||
if (selection.size() == 1) {
|
||||
Object element = selection.getFirstElement();
|
||||
|
||||
// get the project object from it.
|
||||
IProject project = null;
|
||||
if (element instanceof IProject) {
|
||||
project = (IProject) element;
|
||||
} else if (element instanceof IAdaptable) {
|
||||
project = (IProject) ((IAdaptable) element).getAdapter(IProject.class);
|
||||
}
|
||||
|
||||
// and finally do the action
|
||||
if (project != null) {
|
||||
ExportHelper.exportProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
this.mSelection = selection;
|
||||
}
|
||||
}
|
||||
@@ -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.adt.project.export.ExportWizard;
|
||||
|
||||
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 ExportWizardAction 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 export wizard on the current selection.
|
||||
ExportWizard wizard = new ExportWizard();
|
||||
wizard.init(mWorkbench, selection);
|
||||
WizardDialog dialog = new WizardDialog(mWorkbench.getDisplay().getActiveShell(),
|
||||
wizard);
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
this.mSelection = selection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationType;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
|
||||
import org.eclipse.debug.core.ILaunchManager;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to fix the launch configuration of a project if the java package
|
||||
* defined in the manifest has been changed.<br>
|
||||
* This fix can be done synchronously, or asynchronously.<br>
|
||||
* <code>start()</code> will start a thread that will do the fix.<br>
|
||||
* <code>run()</code> will do the fix in the current thread.<br><br>
|
||||
* By default, the fix first display a dialog to the user asking if he/she wants to
|
||||
* do the fix. This can be overriden by calling <code>setDisplayPrompt(false)</code>.
|
||||
*
|
||||
*/
|
||||
public class FixLaunchConfig extends Thread {
|
||||
|
||||
private IProject mProject;
|
||||
private String mOldPackage;
|
||||
private String mNewPackage;
|
||||
|
||||
private boolean mDisplayPrompt = true;
|
||||
|
||||
public FixLaunchConfig(IProject project, String oldPackage, String newPackage) {
|
||||
super();
|
||||
|
||||
mProject = project;
|
||||
mOldPackage = oldPackage;
|
||||
mNewPackage = newPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the display prompt. If true run()/start() first ask the user if he/she wants
|
||||
* to fix the Launch Config
|
||||
* @param displayPrompt
|
||||
*/
|
||||
public void setDisplayPrompt(boolean displayPrompt) {
|
||||
mDisplayPrompt = displayPrompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the Launch configurations.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (mDisplayPrompt) {
|
||||
// ask the user if he really wants to fix the launch config
|
||||
boolean res = AdtPlugin.displayPrompt(
|
||||
"Launch Configuration Update",
|
||||
"The package definition in the manifest changed.\nDo you want to update your Launch Configuration(s)?");
|
||||
|
||||
if (res == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// get the list of config for the project
|
||||
String projectName = mProject.getName();
|
||||
ILaunchConfiguration[] configs = findConfigs(mProject.getName());
|
||||
|
||||
// loop through all the config and update the package
|
||||
for (ILaunchConfiguration config : configs) {
|
||||
try {
|
||||
// get the working copy so that we can make changes.
|
||||
ILaunchConfigurationWorkingCopy copy = config.getWorkingCopy();
|
||||
|
||||
// get the attributes for the activity
|
||||
String activity = config.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY,
|
||||
""); //$NON-NLS-1$
|
||||
|
||||
// manifests can define activities that are not in the defined package,
|
||||
// so we need to make sure the activity is inside the old package.
|
||||
if (activity.startsWith(mOldPackage)) {
|
||||
// create the new activity
|
||||
activity = mNewPackage + activity.substring(mOldPackage.length());
|
||||
|
||||
// put it in the copy
|
||||
copy.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, activity);
|
||||
|
||||
// save the config
|
||||
copy.doSave();
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// couldn't get the working copy. we output the error in the console
|
||||
String msg = String.format("Failed to modify %1$s: %2$s", projectName,
|
||||
e.getMessage());
|
||||
AdtPlugin.printErrorToConsole(mProject, msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for and returns all existing Launch Configuration object for a
|
||||
* specified project.
|
||||
* @param projectName The name of the project
|
||||
* @return all the ILaunchConfiguration object. If none are present, an empty array is
|
||||
* returned.
|
||||
*/
|
||||
private static ILaunchConfiguration[] findConfigs(String projectName) {
|
||||
// get the launch manager
|
||||
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
|
||||
|
||||
// now get the config type for our particular android type.
|
||||
ILaunchConfigurationType configType = manager.
|
||||
getLaunchConfigurationType(LaunchConfigDelegate.ANDROID_LAUNCH_TYPE_ID);
|
||||
|
||||
// create a temp list to hold all the valid configs
|
||||
ArrayList<ILaunchConfiguration> list = new ArrayList<ILaunchConfiguration>();
|
||||
|
||||
try {
|
||||
ILaunchConfiguration[] configs = manager.getLaunchConfigurations(configType);
|
||||
|
||||
for (ILaunchConfiguration config : configs) {
|
||||
if (config.getAttribute(
|
||||
IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
|
||||
"").equals(projectName)) { //$NON-NLS-1$
|
||||
list.add(config);
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
}
|
||||
|
||||
return list.toArray(new ILaunchConfiguration[list.size()]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.eclipse.org/org/documents/epl-v10.php
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.eclipse.adt.project;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.ui.IObjectActionDelegate;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
import org.eclipse.ui.IWorkbenchWindow;
|
||||
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Action to fix the project properties:
|
||||
* <ul>
|
||||
* <li>Make sure the framework archive is present with the link to the java
|
||||
* doc</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FixProjectAction implements IObjectActionDelegate {
|
||||
|
||||
private ISelection mSelection;
|
||||
|
||||
/**
|
||||
* @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
|
||||
*/
|
||||
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
||||
}
|
||||
|
||||
public void run(IAction action) {
|
||||
if (mSelection instanceof IStructuredSelection) {
|
||||
|
||||
for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator();
|
||||
it.hasNext();) {
|
||||
Object element = it.next();
|
||||
IProject project = null;
|
||||
if (element instanceof IProject) {
|
||||
project = (IProject) element;
|
||||
} else if (element instanceof IAdaptable) {
|
||||
project = (IProject) ((IAdaptable) element)
|
||||
.getAdapter(IProject.class);
|
||||
}
|
||||
if (project != null) {
|
||||
fixProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
this.mSelection = selection;
|
||||
}
|
||||
|
||||
private void fixProject(final IProject project) {
|
||||
new Job("Fix Project Properties") {
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
if (monitor != null) {
|
||||
monitor.beginTask("Fix Project Properties", 6);
|
||||
}
|
||||
|
||||
ProjectHelper.fixProject(project);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
// fix the nature order to have the proper project icon
|
||||
ProjectHelper.fixProjectNatureOrder(project);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
// now we fix the builders
|
||||
AndroidNature.configureResourceManagerBuilder(project);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
AndroidNature.configurePreBuilder(project);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
AndroidNature.configureApkBuilder(project);
|
||||
if (monitor != null) {
|
||||
monitor.worked(1);
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
} catch (JavaModelException e) {
|
||||
return e.getJavaModelStatus();
|
||||
} catch (CoreException e) {
|
||||
return e.getStatus();
|
||||
} finally {
|
||||
if (monitor != null) {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IWorkbenchWindowActionDelegate#init
|
||||
*/
|
||||
public void init(IWorkbenchWindow window) {
|
||||
// pass
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.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;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.launching.JavaRuntime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility class to manipulate Project parameters/properties.
|
||||
*/
|
||||
public final class ProjectHelper {
|
||||
public final static int COMPILER_COMPLIANCE_OK = 0;
|
||||
public final static int COMPILER_COMPLIANCE_LEVEL = 1;
|
||||
public final static int COMPILER_COMPLIANCE_SOURCE = 2;
|
||||
public final static int COMPILER_COMPLIANCE_CODEGEN_TARGET = 3;
|
||||
|
||||
/**
|
||||
* Adds the corresponding source folder to the class path entries.
|
||||
*
|
||||
* @param entries The class path entries to read. A copy will be returned.
|
||||
* @param new_entry The parent source folder to remove.
|
||||
* @return A new class path entries array.
|
||||
*/
|
||||
public static IClasspathEntry[] addEntryToClasspath(
|
||||
IClasspathEntry[] entries, IClasspathEntry new_entry) {
|
||||
int n = entries.length;
|
||||
IClasspathEntry[] newEntries = new IClasspathEntry[n + 1];
|
||||
System.arraycopy(entries, 0, newEntries, 0, n);
|
||||
newEntries[n] = new_entry;
|
||||
return newEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a classpath entry from the array.
|
||||
* @param entries The class path entries to read. A copy will be returned
|
||||
* @param index The index to remove.
|
||||
* @return A new class path entries array.
|
||||
*/
|
||||
public static IClasspathEntry[] removeEntryFromClasspath(
|
||||
IClasspathEntry[] entries, int index) {
|
||||
int n = entries.length;
|
||||
IClasspathEntry[] newEntries = new IClasspathEntry[n-1];
|
||||
|
||||
// copy the entries before index
|
||||
System.arraycopy(entries, 0, newEntries, 0, index);
|
||||
|
||||
// copy the entries after index
|
||||
System.arraycopy(entries, index + 1, newEntries, index,
|
||||
entries.length - index - 1);
|
||||
|
||||
return newEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a OS specific path into a path valid for the java doc location
|
||||
* attributes of a project.
|
||||
* @param javaDocOSLocation The OS specific path.
|
||||
* @return a valid path for the java doc location.
|
||||
*/
|
||||
public static String getJavaDocPath(String javaDocOSLocation) {
|
||||
// first thing we do is convert the \ into /
|
||||
String javaDoc = javaDocOSLocation.replaceAll("\\\\", //$NON-NLS-1$
|
||||
AndroidConstants.WS_SEP);
|
||||
|
||||
// then we add file: at the beginning for unix path, and file:/ for non
|
||||
// unix path
|
||||
if (javaDoc.startsWith(AndroidConstants.WS_SEP)) {
|
||||
return "file:" + javaDoc; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
return "file:/" + javaDoc; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a specific classpath entry by full path and return its index.
|
||||
* @param entries The entry array to search in.
|
||||
* @param entryPath The OS specific path of the entry.
|
||||
* @param entryKind The kind of the entry. Accepted values are 0
|
||||
* (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
|
||||
* IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
|
||||
* and IClasspathEntry.CPE_CONTAINER
|
||||
* @return the index of the found classpath entry or -1.
|
||||
*/
|
||||
public static int findClasspathEntryByPath(IClasspathEntry[] entries,
|
||||
String entryPath, int entryKind) {
|
||||
for (int i = 0 ; i < entries.length ; i++) {
|
||||
IClasspathEntry entry = entries[i];
|
||||
|
||||
int kind = entry.getEntryKind();
|
||||
|
||||
if (kind == entryKind || entryKind == 0) {
|
||||
// get the path
|
||||
IPath path = entry.getPath();
|
||||
|
||||
String osPathString = path.toOSString();
|
||||
if (osPathString.equals(entryPath)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found, return bad index.
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a specific classpath entry for file name only and return its
|
||||
* index.
|
||||
* @param entries The entry array to search in.
|
||||
* @param entryName The filename of the entry.
|
||||
* @param entryKind The kind of the entry. Accepted values are 0
|
||||
* (no filter), IClasspathEntry.CPE_LIBRARY, IClasspathEntry.CPE_PROJECT,
|
||||
* IClasspathEntry.CPE_SOURCE, IClasspathEntry.CPE_VARIABLE,
|
||||
* and IClasspathEntry.CPE_CONTAINER
|
||||
* @param startIndex Index where to start the search
|
||||
* @return the index of the found classpath entry or -1.
|
||||
*/
|
||||
public static int findClasspathEntryByName(IClasspathEntry[] entries,
|
||||
String entryName, int entryKind, int startIndex) {
|
||||
if (startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
for (int i = startIndex ; i < entries.length ; i++) {
|
||||
IClasspathEntry entry = entries[i];
|
||||
|
||||
int kind = entry.getEntryKind();
|
||||
|
||||
if (kind == entryKind || entryKind == 0) {
|
||||
// get the path
|
||||
IPath path = entry.getPath();
|
||||
String name = path.segment(path.segmentCount()-1);
|
||||
|
||||
if (name.equals(entryName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found, return bad index.
|
||||
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.
|
||||
* @throws JavaModelException
|
||||
*/
|
||||
public static void fixProject(IProject project) throws JavaModelException {
|
||||
if (AdtPlugin.getOsSdkFolder().length() == 0) {
|
||||
AdtPlugin.printToConsole(project, "Unknown SDK Location, project not fixed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// get a java project
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
fixProjectClasspathEntries(javaProject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the project classpath entries. The method ensures that:
|
||||
* <ul>
|
||||
* <li>The project does not reference any old android.zip/android.jar archive.</li>
|
||||
* <li>The project does not use its output folder as a sourc folder.</li>
|
||||
* <li>The project does not reference a desktop JRE</li>
|
||||
* <li>The project references the AndroidClasspathContainer.
|
||||
* </ul>
|
||||
* @param javaProject The project to fix.
|
||||
* @throws JavaModelException
|
||||
*/
|
||||
public static void fixProjectClasspathEntries(IJavaProject javaProject)
|
||||
throws JavaModelException {
|
||||
|
||||
// get the project classpath
|
||||
IClasspathEntry[] entries = javaProject.getRawClasspath();
|
||||
IClasspathEntry[] oldEntries = entries;
|
||||
|
||||
// check if the JRE is set as library
|
||||
int jreIndex = ProjectHelper.findClasspathEntryByPath(entries, JavaRuntime.JRE_CONTAINER,
|
||||
IClasspathEntry.CPE_CONTAINER);
|
||||
if (jreIndex != -1) {
|
||||
// the project has a JRE included, we remove it
|
||||
entries = ProjectHelper.removeEntryFromClasspath(entries, jreIndex);
|
||||
}
|
||||
|
||||
// get the output folder
|
||||
IPath outputFolder = javaProject.getOutputLocation();
|
||||
|
||||
boolean foundContainer = false;
|
||||
|
||||
for (int i = 0 ; i < entries.length ;) {
|
||||
// get the entry and kind
|
||||
IClasspathEntry entry = entries[i];
|
||||
int kind = entry.getEntryKind();
|
||||
|
||||
if (kind == IClasspathEntry.CPE_SOURCE) {
|
||||
IPath path = entry.getPath();
|
||||
|
||||
if (path.equals(outputFolder)) {
|
||||
entries = ProjectHelper.removeEntryFromClasspath(entries, i);
|
||||
|
||||
// continue, to skip the i++;
|
||||
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)) {
|
||||
foundContainer = true;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// if the framework container is not there, we add it
|
||||
if (foundContainer == false) {
|
||||
// add the android container to the array
|
||||
entries = ProjectHelper.addEntryToClasspath(entries,
|
||||
AndroidClasspathContainerInitializer.getContainerEntry());
|
||||
}
|
||||
|
||||
// set the new list of entries to the project
|
||||
if (entries != oldEntries) {
|
||||
javaProject.setRawClasspath(entries, new NullProgressMonitor());
|
||||
}
|
||||
|
||||
// If needed, check and fix compiler compliance and source compatibility
|
||||
ProjectHelper.checkAndFixCompilerCompliance(javaProject);
|
||||
}
|
||||
/**
|
||||
* Checks the project compiler compliance level is supported.
|
||||
* @param javaProject The project to check
|
||||
* @return <ul>
|
||||
* <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final int checkCompilerCompliance(IJavaProject javaProject) {
|
||||
// get the project compliance level option
|
||||
String compliance = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
|
||||
|
||||
// check it against a list of valid compliance level strings.
|
||||
if (checkCompliance(compliance) == false) {
|
||||
// if we didn't find the proper compliance level, we return an error
|
||||
return COMPILER_COMPLIANCE_LEVEL;
|
||||
}
|
||||
|
||||
// otherwise we check source compatibility
|
||||
String source = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
|
||||
|
||||
// check it against a list of valid compliance level strings.
|
||||
if (checkCompliance(source) == false) {
|
||||
// if we didn't find the proper compliance level, we return an error
|
||||
return COMPILER_COMPLIANCE_SOURCE;
|
||||
}
|
||||
|
||||
// otherwise check codegen level
|
||||
String codeGen = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
|
||||
|
||||
// check it against a list of valid compliance level strings.
|
||||
if (checkCompliance(codeGen) == false) {
|
||||
// if we didn't find the proper compliance level, we return an error
|
||||
return COMPILER_COMPLIANCE_CODEGEN_TARGET;
|
||||
}
|
||||
|
||||
return COMPILER_COMPLIANCE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the project compiler compliance level is supported.
|
||||
* @param project The project to check
|
||||
* @return <ul>
|
||||
* <li><code>COMPILER_COMPLIANCE_OK</code> if the project is properly configured</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_LEVEL</code> for unsupported compiler level</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_SOURCE</code> for unsupported source compatibility</li>
|
||||
* <li><code>COMPILER_COMPLIANCE_CODEGEN_TARGET</code> for unsupported .class format</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final int checkCompilerCompliance(IProject project) {
|
||||
// get the java project from the IProject resource object
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
// check and return the result.
|
||||
return checkCompilerCompliance(javaProject);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks, and fixes if needed, the compiler compliance level, and the source compatibility
|
||||
* level
|
||||
* @param project The project to check and fix.
|
||||
*/
|
||||
public static final void checkAndFixCompilerCompliance(IProject project) {
|
||||
// get the java project from the IProject resource object
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
// Now we check the compiler compliance level and make sure it is valid
|
||||
checkAndFixCompilerCompliance(javaProject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, and fixes if needed, the compiler compliance level, and the source compatibility
|
||||
* level
|
||||
* @param javaProject The Java project to check and fix.
|
||||
*/
|
||||
public static final void checkAndFixCompilerCompliance(IJavaProject javaProject) {
|
||||
if (checkCompilerCompliance(javaProject) != COMPILER_COMPLIANCE_OK) {
|
||||
// setup the preferred compiler compliance level.
|
||||
javaProject.setOption(JavaCore.COMPILER_COMPLIANCE,
|
||||
AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
|
||||
javaProject.setOption(JavaCore.COMPILER_SOURCE,
|
||||
AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
|
||||
javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
|
||||
AndroidConstants.COMPILER_COMPLIANCE_PREFERRED);
|
||||
|
||||
// clean the project to make sure we recompile
|
||||
try {
|
||||
javaProject.getProject().build(IncrementalProjectBuilder.CLEAN_BUILD,
|
||||
new NullProgressMonitor());
|
||||
} catch (CoreException e) {
|
||||
AdtPlugin.printErrorToConsole(javaProject.getProject(),
|
||||
"Project compiler settings changed. Clean your project.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link IProject} by its running application name, as it returned by the VM.
|
||||
* <p/>
|
||||
* <var>applicationName</var> will in most case be the package declared in the manifest, but
|
||||
* can, in some cases, be a custom process name declared in the manifest, in the
|
||||
* <code>application</code>, <code>activity</code>, <code>receiver</code>, or
|
||||
* <code>service</code> nodes.
|
||||
* @param applicationName The application name.
|
||||
* @return a project or <code>null</code> if no matching project were found.
|
||||
*/
|
||||
public static IProject findAndroidProjectByAppName(String applicationName) {
|
||||
// Get the list of project for the current workspace
|
||||
IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||
IProject[] projects = workspace.getRoot().getProjects();
|
||||
|
||||
// look for a project that matches the packageName of the app
|
||||
// we're trying to debug
|
||||
for (IProject p : projects) {
|
||||
if (p.isOpen()) {
|
||||
try {
|
||||
if (p.hasNature(AndroidConstants.NATURE) == false) {
|
||||
// ignore non android projects
|
||||
continue;
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// failed to get the nature? skip project.
|
||||
continue;
|
||||
}
|
||||
|
||||
AndroidManifestHelper androidManifest = new AndroidManifestHelper(p);
|
||||
|
||||
// check that there is indeed a manifest file.
|
||||
if (androidManifest.getManifestIFile() == null) {
|
||||
// no file? skip this project.
|
||||
continue;
|
||||
}
|
||||
|
||||
AndroidManifestParser parser = null;
|
||||
try {
|
||||
parser = AndroidManifestParser.parseForData(
|
||||
androidManifest.getManifestIFile());
|
||||
} catch (CoreException e) {
|
||||
// skip this project.
|
||||
continue;
|
||||
}
|
||||
|
||||
String manifestPackage = parser.getPackage();
|
||||
|
||||
if (manifestPackage != null && manifestPackage.equals(applicationName)) {
|
||||
// this is the project we were looking for!
|
||||
return p;
|
||||
} else {
|
||||
// if the package and application name don't match,
|
||||
// we look for other possible process names declared in the manifest.
|
||||
String[] processes = parser.getProcesses();
|
||||
for (String process : processes) {
|
||||
if (process.equals(applicationName)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public static void fixProjectNatureOrder(IProject project) throws CoreException {
|
||||
IProjectDescription description = project.getDescription();
|
||||
String[] natures = description.getNatureIds();
|
||||
|
||||
// if the android nature is not the first one, we reorder them
|
||||
if (AndroidConstants.NATURE.equals(natures[0]) == false) {
|
||||
// look for the index
|
||||
for (int i = 0 ; i < natures.length ; i++) {
|
||||
if (AndroidConstants.NATURE.equals(natures[i])) {
|
||||
// if we try to just reorder the array in one pass, this doesn't do
|
||||
// anything. I guess JDT check that we are actually adding/removing nature.
|
||||
// So, first we'll remove the android nature, and then add it back.
|
||||
|
||||
// remove the android nature
|
||||
removeNature(project, AndroidConstants.NATURE);
|
||||
|
||||
// now add it back at the first index.
|
||||
description = project.getDescription();
|
||||
natures = description.getNatureIds();
|
||||
|
||||
String[] newNatures = new String[natures.length + 1];
|
||||
|
||||
// first one is android
|
||||
newNatures[0] = AndroidConstants.NATURE;
|
||||
|
||||
// next the rest that was before the android nature
|
||||
System.arraycopy(natures, 0, newNatures, 1, natures.length);
|
||||
|
||||
// set the new natures
|
||||
description.setNatureIds(newNatures);
|
||||
project.setDescription(description, null);
|
||||
|
||||
// and stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a specific nature from a project.
|
||||
* @param project The project to remove the nature from.
|
||||
* @param nature The nature id to remove.
|
||||
* @throws CoreException
|
||||
*/
|
||||
public static void removeNature(IProject project, String nature) throws CoreException {
|
||||
IProjectDescription description = project.getDescription();
|
||||
String[] natures = description.getNatureIds();
|
||||
|
||||
// check if the project already has the android nature.
|
||||
for (int i = 0; i < natures.length; ++i) {
|
||||
if (nature.equals(natures[i])) {
|
||||
String[] newNatures = new String[natures.length - 1];
|
||||
if (i > 0) {
|
||||
System.arraycopy(natures, 0, newNatures, 0, i);
|
||||
}
|
||||
System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1);
|
||||
description.setNatureIds(newNatures);
|
||||
project.setDescription(description, null);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the project has error level markers.
|
||||
* @param includeReferencedProjects flag to also test the referenced projects.
|
||||
* @throws CoreException
|
||||
*/
|
||||
public static boolean hasError(IProject project, boolean includeReferencedProjects)
|
||||
throws CoreException {
|
||||
IMarker[] markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
|
||||
if (markers != null && markers.length > 0) {
|
||||
// the project has marker(s). even though they are "problem" we
|
||||
// don't know their severity. so we loop on them and figure if they
|
||||
// are warnings or errors
|
||||
for (IMarker m : markers) {
|
||||
int s = m.getAttribute(IMarker.SEVERITY, -1);
|
||||
if (s == IMarker.SEVERITY_ERROR) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test the referenced projects if needed.
|
||||
if (includeReferencedProjects) {
|
||||
IProject[] projects = getReferencedProjects(project);
|
||||
|
||||
for (IProject p : projects) {
|
||||
if (hasError(p, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a String property into the persistent storage of a resource.
|
||||
* @param resource The resource into which the string value is saved.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param value the value to save
|
||||
* @return true if the save succeeded.
|
||||
*/
|
||||
public static boolean saveStringProperty(IResource resource, String propertyName,
|
||||
String value) {
|
||||
QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
|
||||
|
||||
try {
|
||||
resource.setPersistentProperty(qname, value);
|
||||
} catch (CoreException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a String property from the persistent storage of a resource.
|
||||
* @param resource The resource from which the string value is loaded.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @return the property value or null if it was not found.
|
||||
*/
|
||||
public static String loadStringProperty(IResource resource, String propertyName) {
|
||||
QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, propertyName);
|
||||
|
||||
try {
|
||||
String value = resource.getPersistentProperty(qname);
|
||||
return value;
|
||||
} catch (CoreException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a property into the persistent storage of a resource.
|
||||
* @param resource The resource into which the boolean value is saved.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param value the value to save
|
||||
* @return true if the save succeeded.
|
||||
*/
|
||||
public static boolean saveBooleanProperty(IResource resource, String propertyName,
|
||||
boolean value) {
|
||||
return saveStringProperty(resource, propertyName, Boolean.toString(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a boolean property from the persistent storage of the project.
|
||||
* @param resource The resource from which the boolean value is loaded.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param defaultValue The default value to return if the property was not found.
|
||||
* @return the property value or the default value if the property was not found.
|
||||
*/
|
||||
public static boolean loadBooleanProperty(IResource resource, String propertyName,
|
||||
boolean defaultValue) {
|
||||
String value = loadStringProperty(resource, propertyName);
|
||||
if (value != null) {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the path of a resource into the persistent storate of the project.
|
||||
* @param resource The resource into which the resource path is saved.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @param value The resource to save. It's its path that is actually stored. If null, an
|
||||
* empty string is stored.
|
||||
* @return true if the save succeeded
|
||||
*/
|
||||
public static boolean saveResourceProperty(IResource resource, String propertyName,
|
||||
IResource value) {
|
||||
if (value != null) {
|
||||
IPath iPath = value.getProjectRelativePath();
|
||||
return saveStringProperty(resource, propertyName, iPath.toString());
|
||||
}
|
||||
|
||||
return saveStringProperty(resource, propertyName, ""); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the path of a resource from the persistent storage of the project, and returns the
|
||||
* corresponding IResource object, if it exists in the same project as <code>resource</code>.
|
||||
* @param resource The resource from which the resource path is loaded.
|
||||
* @param propertyName the name of the property. The id of the plugin is added to this string.
|
||||
* @return The corresponding IResource object (or children interface) or null
|
||||
*/
|
||||
public static IResource loadResourceProperty(IResource resource, String propertyName) {
|
||||
String value = loadStringProperty(resource, propertyName);
|
||||
|
||||
if (value != null && value.length() > 0) {
|
||||
return resource.getProject().findMember(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of referenced project that are opened and Java projects.
|
||||
* @param project
|
||||
* @return list of opened referenced java project.
|
||||
* @throws CoreException
|
||||
*/
|
||||
public static IProject[] getReferencedProjects(IProject project) throws CoreException {
|
||||
IProject[] projects = project.getReferencedProjects();
|
||||
|
||||
ArrayList<IProject> list = new ArrayList<IProject>();
|
||||
|
||||
for (IProject p : projects) {
|
||||
if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
|
||||
list.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new IProject[list.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks a Java project compiler level option against a list of supported versions.
|
||||
* @param optionValue the Compiler level option.
|
||||
* @return true if the option value is supproted.
|
||||
*/
|
||||
private static boolean checkCompliance(String optionValue) {
|
||||
for (String s : AndroidConstants.COMPILER_COMPLIANCE) {
|
||||
if (s != null && s.equals(optionValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
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.ui.IExportWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* Export wizard to export an apk signed with a release key/certificate.
|
||||
*/
|
||||
public 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$
|
||||
|
||||
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
|
||||
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
|
||||
static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
|
||||
*/
|
||||
static abstract class ExportWizardPage extends WizardPage {
|
||||
|
||||
protected boolean mNewProjectReference = true;
|
||||
|
||||
ExportWizardPage(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
abstract void onShow();
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
if (visible) {
|
||||
onShow();
|
||||
mNewProjectReference = false;
|
||||
}
|
||||
}
|
||||
|
||||
void newProjectReference() {
|
||||
mNewProjectReference = true;
|
||||
}
|
||||
}
|
||||
|
||||
private ExportWizardPage mPages[] = new ExportWizardPage[3];
|
||||
|
||||
private IProject mProject;
|
||||
|
||||
private String mKeystore;
|
||||
private String mKeyAlias;
|
||||
private char[] mKeystorePassword;
|
||||
private char[] mKeyPassword;
|
||||
|
||||
private PrivateKey mPrivateKey;
|
||||
private X509Certificate mCertificate;
|
||||
|
||||
private String mDestinationPath;
|
||||
private String mApkFilePath;
|
||||
private String mApkFileName;
|
||||
|
||||
public ExportWizard() {
|
||||
setHelpAvailable(false); // TODO have help
|
||||
setWindowTitle("Export Android Application");
|
||||
setImageDescriptor();
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performFinish() {
|
||||
// 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 {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
builder.close();
|
||||
fos.close();
|
||||
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFinish() {
|
||||
return mApkFilePath != null &&
|
||||
mPrivateKey != null && mCertificate != null &&
|
||||
mDestinationPath != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
|
||||
*/
|
||||
public void init(IWorkbench workbench, IStructuredSelection selection) {
|
||||
// get the project from the selection
|
||||
Object selected = selection.getFirstElement();
|
||||
|
||||
if (selected instanceof IProject) {
|
||||
mProject = (IProject)selected;
|
||||
} else if (selected instanceof IAdaptable) {
|
||||
IResource r = (IResource)((IAdaptable)selected).getAdapter(IResource.class);
|
||||
if (r != null) {
|
||||
mProject = r.getProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image descriptor for the wizard logo.
|
||||
*/
|
||||
private void setImageDescriptor() {
|
||||
ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
|
||||
setDefaultPageImageDescriptor(desc);
|
||||
}
|
||||
|
||||
IProject getProject() {
|
||||
return mProject;
|
||||
}
|
||||
|
||||
void setProject(IProject project, String apkFilePath, String filename) {
|
||||
mProject = project;
|
||||
mApkFilePath = apkFilePath;
|
||||
mApkFileName = filename;
|
||||
|
||||
// indicate to the page that the project was changed.
|
||||
for (ExportWizardPage page : mPages) {
|
||||
page.newProjectReference();
|
||||
}
|
||||
}
|
||||
|
||||
String getApkFilename() {
|
||||
return mApkFileName;
|
||||
}
|
||||
|
||||
void setKeystore(String path) {
|
||||
mKeystore = path;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
}
|
||||
|
||||
String getKeystore() {
|
||||
return mKeystore;
|
||||
}
|
||||
|
||||
void setKeyAlias(String name) {
|
||||
mKeyAlias = name;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
}
|
||||
|
||||
String getKeyAlias() {
|
||||
return mKeyAlias;
|
||||
}
|
||||
|
||||
void setKeystorePassword(char[] password) {
|
||||
mKeystorePassword = password;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
}
|
||||
|
||||
char[] getKeystorePassword() {
|
||||
return mKeystorePassword;
|
||||
}
|
||||
|
||||
void setKeyPassword(char[] password) {
|
||||
mKeyPassword = password;
|
||||
mPrivateKey = null;
|
||||
mCertificate = null;
|
||||
}
|
||||
|
||||
char[] getKeyPassword() {
|
||||
return mKeyPassword;
|
||||
}
|
||||
|
||||
void setSigningInfo(PrivateKey privateKey, X509Certificate certificate) {
|
||||
mPrivateKey = privateKey;
|
||||
mCertificate = certificate;
|
||||
}
|
||||
|
||||
void setDestination(String path) {
|
||||
mDestinationPath = path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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,318 @@
|
||||
/*
|
||||
* 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.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestParser;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
|
||||
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
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.graphics.Image;
|
||||
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.Display;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* First Export Wizard Page. Display warning/errors.
|
||||
*/
|
||||
public class PreExportPage extends ExportWizardPage {
|
||||
private final static String IMG_ERROR = "error.png"; //$NON-NLS-1$
|
||||
private final static String IMG_WARNING = "warning.png"; //$NON-NLS-1$
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private Display mDisplay;
|
||||
private Image mError;
|
||||
private Image mWarning;
|
||||
private boolean mHasMessage = false;
|
||||
private Composite mTopComposite;
|
||||
private Composite mErrorComposite;
|
||||
private Text mProjectText;
|
||||
private ProjectChooserHelper mProjectChooserHelper;
|
||||
|
||||
protected PreExportPage(ExportWizard wizard, String pageName) {
|
||||
super(pageName);
|
||||
mWizard = wizard;
|
||||
|
||||
setTitle("Pre Export Checks");
|
||||
setDescription("Performs a set of checks to make sure the application can be exported.");
|
||||
}
|
||||
|
||||
public void createControl(Composite parent) {
|
||||
mProjectChooserHelper = new ProjectChooserHelper(parent.getShell());
|
||||
mDisplay = parent.getDisplay();
|
||||
|
||||
GridLayout gl = null;
|
||||
GridData gd = null;
|
||||
|
||||
mTopComposite = new Composite(parent, SWT.NONE);
|
||||
mTopComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
mTopComposite.setLayout(new GridLayout(1, false));
|
||||
|
||||
// composite for the project selection.
|
||||
Composite projectComposite = new Composite(mTopComposite, SWT.NONE);
|
||||
projectComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
projectComposite.setLayout(gl = new GridLayout(3, false));
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
|
||||
Label label = new Label(projectComposite, SWT.NONE);
|
||||
label.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.horizontalSpan = 3;
|
||||
label.setText("Select the project to export:");
|
||||
|
||||
new Label(projectComposite, SWT.NONE).setText("Project:");
|
||||
mProjectText = new Text(projectComposite, SWT.BORDER);
|
||||
mProjectText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
mProjectText.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
handleProjectNameChange();
|
||||
}
|
||||
});
|
||||
|
||||
Button browseButton = new Button(projectComposite, SWT.PUSH);
|
||||
browseButton.setText("Browse...");
|
||||
browseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject(
|
||||
mProjectText.getText().trim());
|
||||
|
||||
if (javaProject != null) {
|
||||
IProject project = javaProject.getProject();
|
||||
|
||||
// set the new name in the text field. The modify listener will take
|
||||
// care of updating the status and the ExportWizard object.
|
||||
mProjectText.setText(project.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setControl(mTopComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// get the project and init the ui
|
||||
IProject project = mWizard.getProject();
|
||||
if (project != null) {
|
||||
mProjectText.setText(project.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void buildErrorUi(IProject project) {
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setPageComplete(true);
|
||||
mHasMessage = false;
|
||||
|
||||
// composite parent for the warning/error
|
||||
GridLayout gl = null;
|
||||
mErrorComposite = new Composite(mTopComposite, SWT.NONE);
|
||||
mErrorComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
gl = new GridLayout(2, false);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
gl.verticalSpacing *= 3; // more spacing than normal.
|
||||
mErrorComposite.setLayout(gl);
|
||||
|
||||
if (project == null) {
|
||||
setErrorMessage("Select project to export.");
|
||||
mHasMessage = true;
|
||||
} else {
|
||||
try {
|
||||
if (project.hasNature(AndroidConstants.NATURE) == false) {
|
||||
addError(mErrorComposite, "Project is not an Android project.");
|
||||
} else {
|
||||
// check for errors
|
||||
if (ProjectHelper.hasError(project, true)) {
|
||||
addError(mErrorComposite, "Project has compilation error(s)");
|
||||
}
|
||||
|
||||
// check the project output
|
||||
IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project);
|
||||
if (outputIFolder != null) {
|
||||
String outputOsPath = outputIFolder.getLocation().toOSString();
|
||||
String apkFilePath = outputOsPath + File.separator + project.getName() +
|
||||
AndroidConstants.DOT_ANDROID_PACKAGE;
|
||||
|
||||
File f = new File(apkFilePath);
|
||||
if (f.isFile() == false) {
|
||||
addError(mErrorComposite,
|
||||
String.format("%1$s/%2$s/%1$s%3$s does not exists!",
|
||||
project.getName(),
|
||||
outputIFolder.getName(),
|
||||
AndroidConstants.DOT_ANDROID_PACKAGE));
|
||||
}
|
||||
} else {
|
||||
addError(mErrorComposite,
|
||||
"Unable to get the output folder of the project!");
|
||||
}
|
||||
|
||||
|
||||
// project is an android project, we check the debuggable attribute.
|
||||
AndroidManifestParser manifestParser = AndroidManifestParser.parse(
|
||||
BaseProjectHelper.getJavaProject(project), null /* errorListener */,
|
||||
true /* gatherData */, false /* markErrors */);
|
||||
|
||||
Boolean debuggable = manifestParser.getDebuggable();
|
||||
|
||||
if (debuggable != null && debuggable == Boolean.TRUE) {
|
||||
addWarning(mErrorComposite,
|
||||
"The manifest 'debuggable' attribute is set to true.\nYou should set it to false for applications that you release to the public.");
|
||||
}
|
||||
|
||||
// check for mapview stuff
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// unable to access nature
|
||||
addError(mErrorComposite, "Unable to get project nature");
|
||||
}
|
||||
}
|
||||
|
||||
if (mHasMessage == false) {
|
||||
Label label = new Label(mErrorComposite, SWT.NONE);
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalSpan = 2;
|
||||
label.setLayoutData(gd);
|
||||
label.setText("No errors found. Click Next.");
|
||||
}
|
||||
|
||||
mTopComposite.layout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error label to a {@link Composite} object.
|
||||
* @param parent the Composite parent.
|
||||
* @param message the error message.
|
||||
*/
|
||||
private void addError(Composite parent, String message) {
|
||||
if (mError == null) {
|
||||
mError = AdtPlugin.getImageLoader().loadImage(IMG_ERROR, mDisplay);
|
||||
}
|
||||
|
||||
new Label(parent, SWT.NONE).setImage(mError);
|
||||
Label label = new Label(parent, SWT.NONE);
|
||||
label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
label.setText(message);
|
||||
|
||||
setErrorMessage("Application cannot be exported due to the error(s) below.");
|
||||
setPageComplete(false);
|
||||
mHasMessage = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a warning label to a {@link Composite} object.
|
||||
* @param parent the Composite parent.
|
||||
* @param message the warning message.
|
||||
*/
|
||||
private void addWarning(Composite parent, String message) {
|
||||
if (mWarning == null) {
|
||||
mWarning = AdtPlugin.getImageLoader().loadImage(IMG_WARNING, mDisplay);
|
||||
}
|
||||
|
||||
new Label(parent, SWT.NONE).setImage(mWarning);
|
||||
Label label = new Label(parent, SWT.NONE);
|
||||
label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
label.setText(message);
|
||||
|
||||
mHasMessage = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the parameters for correctness, and update the error message and buttons.
|
||||
* @return the current IProject of this launch config.
|
||||
*/
|
||||
private void handleProjectNameChange() {
|
||||
setPageComplete(false);
|
||||
|
||||
if (mErrorComposite != null) {
|
||||
mErrorComposite.dispose();
|
||||
mErrorComposite = null;
|
||||
}
|
||||
|
||||
// update the wizard with the new project
|
||||
mWizard.setProject(null, null, null);
|
||||
|
||||
//test the project name first!
|
||||
String text = mProjectText.getText().trim();
|
||||
if (text.length() == 0) {
|
||||
setErrorMessage("Select project to export.");
|
||||
} else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) {
|
||||
setErrorMessage("Project name contains unsupported characters!");
|
||||
} else {
|
||||
IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null);
|
||||
IProject found = null;
|
||||
for (IJavaProject javaProject : projects) {
|
||||
if (javaProject.getProject().getName().equals(text)) {
|
||||
found = javaProject.getProject();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (found != null) {
|
||||
setErrorMessage(null);
|
||||
|
||||
// update the wizard with the new project
|
||||
setApkFilePathInWizard(found);
|
||||
|
||||
// now rebuild the error ui.
|
||||
buildErrorUi(found);
|
||||
} else {
|
||||
setErrorMessage(String.format("There is no android project named '%1$s'",
|
||||
text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setApkFilePathInWizard(IProject project) {
|
||||
if (project != null) {
|
||||
IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project);
|
||||
if (outputIFolder != null) {
|
||||
String outputOsPath = outputIFolder.getLocation().toOSString();
|
||||
String apkFilePath = outputOsPath + File.separator + project.getName() +
|
||||
AndroidConstants.DOT_ANDROID_PACKAGE;
|
||||
|
||||
File f = new File(apkFilePath);
|
||||
if (f.isFile()) {
|
||||
mWizard.setProject(project, apkFilePath, f.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mWizard.setProject(null, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 java.io.File;
|
||||
|
||||
/**
|
||||
* Second export wizard page.
|
||||
*/
|
||||
public class SigningExportPage extends ExportWizardPage {
|
||||
|
||||
private final ExportWizard mWizard;
|
||||
private Text mKeystore;
|
||||
private Text mAlias;
|
||||
private Text mKeystorePassword;
|
||||
private Text mKeyPassword;
|
||||
private boolean mDisableOnChange = false;
|
||||
|
||||
protected SigningExportPage(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.");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
new Label(composite, SWT.NONE).setText("Keystore:");
|
||||
mKeystore = new Text(composite, SWT.BORDER);
|
||||
mKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
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.OPEN);
|
||||
fileDialog.setText("Load Keystore");
|
||||
|
||||
String fileName = fileDialog.open();
|
||||
if (fileName != null) {
|
||||
mKeystore.setText(fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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:");
|
||||
mKeystorePassword = new Text(composite, SWT.BORDER | SWT.PASSWORD);
|
||||
mKeystorePassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
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));
|
||||
|
||||
// Show description the first time
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
setControl(composite);
|
||||
|
||||
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());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
mKeyPassword.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
mWizard.setKeyPassword(mKeyPassword.getText().trim().toCharArray());
|
||||
onChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
void onShow() {
|
||||
// fill the texts with information loaded from the project.
|
||||
if (mNewProjectReference) {
|
||||
// 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 keystore = ProjectHelper.loadStringProperty(project,
|
||||
ExportWizard.PROPERTY_KEYSTORE);
|
||||
if (keystore != null) {
|
||||
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$
|
||||
|
||||
// enable onChange, and call it to display errors and enable/disable pageCompleted.
|
||||
mDisableOnChange = false;
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes and update the error message and calls {@link #setPageComplete(boolean)}.
|
||||
*/
|
||||
private void onChange() {
|
||||
if (mDisableOnChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
setErrorMessage(null);
|
||||
setMessage(null);
|
||||
|
||||
// checks the keystore path is non null.
|
||||
String keystore = mKeystore.getText().trim();
|
||||
if (keystore.length() == 0) {
|
||||
setErrorMessage("Enter path to keystore.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else {
|
||||
File f = new File(keystore);
|
||||
if (f.exists() == false) {
|
||||
setErrorMessage("Keystore does not exists!");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
} else if (f.isDirectory()) {
|
||||
setErrorMessage("Keystore is a directory!");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAlias.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key alias.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeystorePassword.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter keystore password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mKeyPassword.getText().trim().length() == 0) {
|
||||
setErrorMessage("Enter key password.");
|
||||
setPageComplete(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setPageComplete(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.project.internal;
|
||||
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.jdt.core.IClasspathContainer;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
|
||||
/**
|
||||
* Classpath container for the Android projects.
|
||||
*/
|
||||
class AndroidClasspathContainer implements IClasspathContainer {
|
||||
|
||||
private IClasspathEntry[] mClasspathEntry;
|
||||
private IPath mContainerPath;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
AndroidClasspathContainer(IClasspathEntry entry, IPath path) {
|
||||
mClasspathEntry = new IClasspathEntry[] { entry };
|
||||
mContainerPath = path;
|
||||
}
|
||||
|
||||
public IClasspathEntry[] getClasspathEntries() {
|
||||
return mClasspathEntry;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Android Library";
|
||||
}
|
||||
|
||||
public int getKind() {
|
||||
return IClasspathContainer.K_DEFAULT_SYSTEM;
|
||||
}
|
||||
|
||||
public IPath getPath() {
|
||||
return mContainerPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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.project.internal;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.jdt.core.ClasspathContainerInitializer;
|
||||
import org.eclipse.jdt.core.IAccessRule;
|
||||
import org.eclipse.jdt.core.IClasspathAttribute;
|
||||
import org.eclipse.jdt.core.IClasspathContainer;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
|
||||
/**
|
||||
* Classpath container initializer responsible for binding {@link AndroidClasspathContainer} to
|
||||
* {@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$
|
||||
|
||||
public AndroidClasspathContainerInitializer() {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a classpath container to a {@link IClasspathContainer} for a given project,
|
||||
* or silently fails if unable to do so.
|
||||
* @param containerPath the container path that is the container id.
|
||||
* @param project the project to bind
|
||||
*/
|
||||
@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),
|
||||
new IJavaProject[] { project },
|
||||
new IClasspathContainer[] { allocateAndroidContainer(id) },
|
||||
new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IClasspathEntry} of type {@link IClasspathEntry#CPE_CONTAINER}
|
||||
* linking to the Android Framework.
|
||||
*/
|
||||
public static IClasspathEntry getContainerEntry() {
|
||||
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.
|
||||
* @param path the <code>IPath</code> to check.
|
||||
*/
|
||||
public static boolean checkPath(IPath path) {
|
||||
return CONTAINER_ID.equals(path.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link IJavaProject} objects with new android framework container. This forces
|
||||
* JDT to recompile them.
|
||||
* @param androidProjects the projects to update.
|
||||
* @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);
|
||||
}
|
||||
|
||||
// give each project their new container in one call.
|
||||
JavaCore.setClasspathContainer(
|
||||
new Path(CONTAINER_ID),
|
||||
androidProjects, containers, new NullProgressMonitor());
|
||||
|
||||
return true;
|
||||
} catch (JavaModelException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and returns an {@link AndroidClasspathContainer} object with the proper
|
||||
* path to the framework jar file.
|
||||
* @param containerId the container id to be used.
|
||||
*/
|
||||
private static IClasspathContainer allocateAndroidContainer(String containerId) {
|
||||
return new AndroidClasspathContainer(createFrameworkClasspath(), new Path(containerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static IClasspathEntry createFrameworkClasspath() {
|
||||
// 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());
|
||||
|
||||
// create the java doc link.
|
||||
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
|
||||
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, AdtPlugin.getUrlDoc());
|
||||
|
||||
// create the access rule to restrict access to classes in com.android.internal
|
||||
IAccessRule accessRule = JavaCore.newAccessRule(
|
||||
new Path("com/android/internal/**"), //$NON-NLS-1$
|
||||
IAccessRule.K_NON_ACCESSIBLE);
|
||||
|
||||
IClasspathEntry classpathEntry = JavaCore.newLibraryEntry(android_lib,
|
||||
android_src, // source attachment path
|
||||
null, // default source attachment root path.
|
||||
new IAccessRule[] { accessRule },
|
||||
new IClasspathAttribute[] { cpAttribute },
|
||||
false // not exported.
|
||||
);
|
||||
|
||||
return classpathEntry;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
* 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.project.internal;
|
||||
|
||||
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.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceStatus;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.ui.actions.OpenJavaPerspectiveAction;
|
||||
import org.eclipse.jface.dialogs.ErrorDialog;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.wizard.Wizard;
|
||||
import org.eclipse.ui.INewWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.actions.WorkspaceModifyOperation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A "New Android Project" Wizard.
|
||||
* <p/>
|
||||
* Note: this class is public so that it can be accessed from unit tests.
|
||||
* It is however an internal class. Its API may change without notice.
|
||||
* It should semantically be considered as a private final class.
|
||||
* Do not derive from this class.
|
||||
|
||||
*/
|
||||
public class NewProjectWizard extends Wizard implements INewWizard {
|
||||
|
||||
private static final String PARAM_SDK_TOOLS_DIR = "ANDROID_SDK_TOOLS"; //$NON-NLS-1$
|
||||
private static final String PARAM_ACTIVITY = "ACTIVITY_NAME"; //$NON-NLS-1$
|
||||
private static final String PARAM_APPLICATION = "APPLICATION_NAME"; //$NON-NLS-1$
|
||||
private static final String PARAM_PACKAGE = "PACKAGE"; //$NON-NLS-1$
|
||||
private static final String PARAM_PROJECT = "PROJECT_NAME"; //$NON-NLS-1$
|
||||
private static final String PARAM_STRING_NAME = "STRING_NAME"; //$NON-NLS-1$
|
||||
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 PH_ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$
|
||||
private static final String PH_INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$
|
||||
private static final String PH_STRINGS = "STRINGS"; //$NON-NLS-1$
|
||||
|
||||
private static final String BIN_DIRECTORY =
|
||||
AndroidConstants.FD_BINARIES + AndroidConstants.WS_SEP;
|
||||
private static final String RES_DIRECTORY =
|
||||
AndroidConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
|
||||
private static final String ASSETS_DIRECTORY =
|
||||
AndroidConstants.FD_ASSETS + AndroidConstants.WS_SEP;
|
||||
private static final String DRAWABLE_DIRECTORY =
|
||||
AndroidConstants.FD_DRAWABLE + AndroidConstants.WS_SEP;
|
||||
private static final String LAYOUT_DIRECTORY =
|
||||
AndroidConstants.FD_LAYOUT + AndroidConstants.WS_SEP;
|
||||
private static final String VALUES_DIRECTORY =
|
||||
AndroidConstants.FD_VALUES + AndroidConstants.WS_SEP;
|
||||
|
||||
private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$
|
||||
private static final String TEMPLATE_MANIFEST = TEMPLATES_DIRECTORY
|
||||
+ "AndroidManifest.template"; //$NON-NLS-1$
|
||||
private static final String TEMPLATE_ACTIVITIES = TEMPLATES_DIRECTORY
|
||||
+ "activity.template"; //$NON-NLS-1$
|
||||
private static final String TEMPLATE_INTENT_LAUNCHER = TEMPLATES_DIRECTORY
|
||||
+ "launcher_intent_filter.template"; //$NON-NLS-1$
|
||||
|
||||
private static final String TEMPLATE_STRINGS = TEMPLATES_DIRECTORY
|
||||
+ "strings.template"; //$NON-NLS-1$
|
||||
private static final String TEMPLATE_STRING = TEMPLATES_DIRECTORY
|
||||
+ "string.template"; //$NON-NLS-1$
|
||||
private static final String ICON = "icon.png"; //$NON-NLS-1$
|
||||
|
||||
private static final String STRINGS_FILE = "strings.xml"; //$NON-NLS-1$
|
||||
|
||||
private static final String STRING_RSRC_PREFIX = "@string/"; //$NON-NLS-1$
|
||||
private static final String STRING_APP_NAME = "app_name"; //$NON-NLS-1$
|
||||
private static final String STRING_HELLO_WORLD = "hello"; //$NON-NLS-1$
|
||||
|
||||
private static final String[] DEFAULT_DIRECTORIES = new String[] {
|
||||
BIN_DIRECTORY, RES_DIRECTORY, ASSETS_DIRECTORY };
|
||||
private static final String[] RES_DIRECTORIES = new String[] {
|
||||
DRAWABLE_DIRECTORY, LAYOUT_DIRECTORY, VALUES_DIRECTORY};
|
||||
|
||||
private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
|
||||
private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$
|
||||
private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$
|
||||
private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$
|
||||
|
||||
protected static final String MAIN_PAGE_NAME = "newAndroidProjectPage"; //$NON-NLS-1$
|
||||
|
||||
private NewProjectCreationPage mMainPage;
|
||||
|
||||
/**
|
||||
* Initializes this creation wizard using the passed workbench and object
|
||||
* selection. Inherited from org.eclipse.ui.IWorkbenchWizard
|
||||
*/
|
||||
public void init(IWorkbench workbench, IStructuredSelection selection) {
|
||||
setHelpAvailable(false); // TODO have help
|
||||
setWindowTitle("New Android Project");
|
||||
setImageDescriptor();
|
||||
|
||||
mMainPage = createMainPage();
|
||||
mMainPage.setTitle("New Android Project");
|
||||
mMainPage.setDescription("Creates a new Android Project resource.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the wizard page.
|
||||
* <p/>
|
||||
* Please do NOT override this method.
|
||||
* <p/>
|
||||
* This is protected so that it can be overridden by unit tests.
|
||||
* However the contract of this class is private and NO ATTEMPT will be made
|
||||
* to maintain compatibility between different versions of the plugin.
|
||||
*/
|
||||
protected NewProjectCreationPage createMainPage() {
|
||||
return new NewProjectCreationPage(MAIN_PAGE_NAME);
|
||||
}
|
||||
|
||||
// -- Methods inherited from org.eclipse.jface.wizard.Wizard --
|
||||
// The Wizard class implements most defaults and boilerplate code needed by
|
||||
// IWizard
|
||||
|
||||
/**
|
||||
* Adds pages to this wizard.
|
||||
*/
|
||||
@Override
|
||||
public void addPages() {
|
||||
addPage(mMainPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any actions appropriate in response to the user having pressed
|
||||
* the Finish button, or refuse if finishing now is not permitted: here, it
|
||||
* actually creates the workspace project and then switch to the Java
|
||||
* perspective.
|
||||
*
|
||||
* @return True
|
||||
*/
|
||||
@Override
|
||||
public boolean performFinish() {
|
||||
if (!createAndroidProject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the default Java Perspective
|
||||
OpenJavaPerspectiveAction action = new OpenJavaPerspectiveAction();
|
||||
action.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
// -- Custom Methods --
|
||||
|
||||
/**
|
||||
* Before actually creating the project for a new project (as opposed to using an
|
||||
* existing project), we check if the target location is a directory that either does
|
||||
* not exist or is empty.
|
||||
*
|
||||
* If it's not empty, ask the user for confirmation.
|
||||
*
|
||||
* @param destination The destination folder where the new project is to be created.
|
||||
* @return True if the destination doesn't exist yet or is an empty directory or is
|
||||
* accepted by the user.
|
||||
*/
|
||||
private boolean validateNewProjectLocationIsEmpty(IPath destination) {
|
||||
File f = new File(destination.toOSString());
|
||||
if (f.isDirectory() && f.list().length > 0) {
|
||||
return AdtPlugin.displayPrompt("New Android Project",
|
||||
"You are going to create a new Android Project in an existing, non-empty, directory. Are you sure you want to proceed?");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the android project.
|
||||
* @return True if the project could be created.
|
||||
*/
|
||||
private boolean createAndroidProject() {
|
||||
IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||
final IProject project = workspace.getRoot().getProject(mMainPage.getProjectName());
|
||||
final IProjectDescription description = workspace.newProjectDescription(project.getName());
|
||||
|
||||
final Map<String, String> parameters = new HashMap<String, String>();
|
||||
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_SRC_FOLDER, mMainPage.getSourceFolder());
|
||||
|
||||
if (mMainPage.isCreateActivity()) {
|
||||
// An activity name can be of the form ".package.Class" or ".Class".
|
||||
// The initial dot is ignored, as it is always added later in the templates.
|
||||
String activityName = mMainPage.getActivityName();
|
||||
if (activityName.startsWith(".")) { //$NON-NLS-1$
|
||||
activityName = activityName.substring(1);
|
||||
}
|
||||
parameters.put(PARAM_ACTIVITY, activityName);
|
||||
}
|
||||
|
||||
// create a dictionary of string that will contain name+content.
|
||||
// we'll put all the strings into values/strings.xml
|
||||
final HashMap<String, String> stringDictionary = new HashMap<String, String>();
|
||||
stringDictionary.put(STRING_APP_NAME, mMainPage.getApplicationName());
|
||||
|
||||
IPath path = mMainPage.getLocationPath();
|
||||
IPath defaultLocation = Platform.getLocation();
|
||||
if (!path.equals(defaultLocation)) {
|
||||
description.setLocation(path);
|
||||
}
|
||||
|
||||
if (mMainPage.isNewProject() && !mMainPage.useDefaultLocation() &&
|
||||
!validateNewProjectLocationIsEmpty(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a monitored operation to create the actual project
|
||||
WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
|
||||
@Override
|
||||
protected void execute(IProgressMonitor monitor) throws InvocationTargetException {
|
||||
createProjectAsync(project, description, monitor, parameters, stringDictionary);
|
||||
}
|
||||
};
|
||||
|
||||
// Run the operation in a different thread
|
||||
runAsyncOperation(op);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the operation in a different thread and display generated
|
||||
* exceptions.
|
||||
*
|
||||
* @param op The asynchronous operation to run.
|
||||
*/
|
||||
private void runAsyncOperation(WorkspaceModifyOperation op) {
|
||||
try {
|
||||
getContainer().run(true /* fork */, true /* cancelable */, op);
|
||||
} catch (InvocationTargetException e) {
|
||||
// The runnable threw an exception
|
||||
Throwable t = e.getTargetException();
|
||||
if (t instanceof CoreException) {
|
||||
CoreException core = (CoreException) t;
|
||||
if (core.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
|
||||
// The error indicates the file system is not case sensitive
|
||||
// and there's a resource with a similar name.
|
||||
MessageDialog.openError(getShell(), "Error", "Error: Case Variant Exists");
|
||||
} else {
|
||||
ErrorDialog.openError(getShell(), "Error", null, core.getStatus());
|
||||
}
|
||||
} else {
|
||||
// Some other kind of exception
|
||||
MessageDialog.openError(getShell(), "Error", t.getMessage());
|
||||
}
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the actual project, sets its nature and adds the required folders
|
||||
* and files to it. This is run asynchronously in a different thread.
|
||||
*
|
||||
* @param project The project to create.
|
||||
* @param description A description of the project.
|
||||
* @param monitor An existing monitor.
|
||||
* @param parameters Template parameters.
|
||||
* @param stringDictionary String definition.
|
||||
* @throws InvocationTargetException to wrap any unmanaged exception and
|
||||
* return it to the calling thread. The method can fail if it fails
|
||||
* 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,
|
||||
Map<String, String> stringDictionary)
|
||||
throws InvocationTargetException {
|
||||
monitor.beginTask("Create Android Project", 100);
|
||||
try {
|
||||
// Create project and open it
|
||||
project.create(description, new SubProgressMonitor(monitor, 10));
|
||||
if (monitor.isCanceled()) throw new OperationCanceledException();
|
||||
project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 10));
|
||||
|
||||
// Add the Java and android nature to the project
|
||||
AndroidNature.setupProjectNatures(project, monitor);
|
||||
|
||||
// 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) };
|
||||
addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolder, monitor);
|
||||
|
||||
// Create the resource folders in the project if they don't already exist.
|
||||
addDefaultDirectories(project, RES_DIRECTORY, RES_DIRECTORIES, monitor);
|
||||
|
||||
// Setup class path
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
setupSourceFolder(javaProject, sourceFolder[0], monitor);
|
||||
|
||||
if (Boolean.parseBoolean(parameters.get(PARAM_IS_NEW_PROJECT))) {
|
||||
// Create files in the project if they don't already exist
|
||||
addManifest(project, parameters, stringDictionary, monitor);
|
||||
|
||||
// add the default app icon
|
||||
addIcon(project, monitor);
|
||||
|
||||
// Create the default package components
|
||||
addSampleCode(project, sourceFolder[0], parameters, stringDictionary, monitor);
|
||||
|
||||
// add the string definition file if needed
|
||||
if (stringDictionary.size() > 0) {
|
||||
addStringDictionaryFile(project, stringDictionary, monitor);
|
||||
}
|
||||
|
||||
// Set output location
|
||||
javaProject.setOutputLocation(project.getFolder(BIN_DIRECTORY).getFullPath(),
|
||||
monitor);
|
||||
}
|
||||
|
||||
// Fix the project to make sure all properties are as expected.
|
||||
// Necessary for existing projects and good for new ones to.
|
||||
ProjectHelper.fixProject(project);
|
||||
|
||||
} catch (CoreException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
} catch (IOException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
} finally {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default directories to the project.
|
||||
*
|
||||
* @param project The Java Project to update.
|
||||
* @param parentFolder The path of the parent folder. Must end with a
|
||||
* separator.
|
||||
* @param folders Folders to be added.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws CoreException if the method fails to create the directories in
|
||||
* the project.
|
||||
*/
|
||||
private void addDefaultDirectories(IProject project, String parentFolder,
|
||||
String[] folders, IProgressMonitor monitor) throws CoreException {
|
||||
for (String name : folders) {
|
||||
if (name.length() > 0) {
|
||||
IFolder folder = project.getFolder(parentFolder + name);
|
||||
if (!folder.exists()) {
|
||||
folder.create(true /* force */, true /* local */,
|
||||
new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the manifest to the project.
|
||||
*
|
||||
* @param project The Java Project to update.
|
||||
* @param parameters Template Parameters.
|
||||
* @param stringDictionary String List to be added to a string definition
|
||||
* file. This map will be filled by this method.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws CoreException if the method fails to update the project.
|
||||
* @throws IOException if the method fails to create the files in the
|
||||
* project.
|
||||
*/
|
||||
private void addManifest(IProject project, Map<String, String> parameters,
|
||||
Map<String, String> stringDictionary, IProgressMonitor monitor)
|
||||
throws CoreException, IOException {
|
||||
|
||||
// get IFile to the manifest and check if it's not already there.
|
||||
IFile file = project.getFile(AndroidConstants.FN_ANDROID_MANIFEST);
|
||||
if (!file.exists()) {
|
||||
|
||||
// Read manifest template
|
||||
String manifestTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_MANIFEST);
|
||||
|
||||
// Replace all keyword parameters
|
||||
manifestTemplate = replaceParameters(manifestTemplate, parameters);
|
||||
|
||||
if (parameters.containsKey(PARAM_ACTIVITY)) {
|
||||
// now get the activity template
|
||||
String activityTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_ACTIVITIES);
|
||||
|
||||
// Replace all keyword parameters to make main activity.
|
||||
String activities = replaceParameters(activityTemplate, parameters);
|
||||
|
||||
// set the intent.
|
||||
String intent = AdtPlugin.readEmbeddedTextFile(TEMPLATE_INTENT_LAUNCHER);
|
||||
|
||||
// set the intent to the main activity
|
||||
activities = activities.replaceAll(PH_INTENT_FILTERS, intent);
|
||||
|
||||
// set the activity(ies) in the manifest
|
||||
manifestTemplate = manifestTemplate.replaceAll(PH_ACTIVITIES, activities);
|
||||
} else {
|
||||
// remove the activity(ies) from the manifest
|
||||
manifestTemplate = manifestTemplate.replaceAll(PH_ACTIVITIES, "");
|
||||
}
|
||||
|
||||
// Save in the project as UTF-8
|
||||
InputStream stream = new ByteArrayInputStream(
|
||||
manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$
|
||||
file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the string resource file.
|
||||
*
|
||||
* @param project The Java Project to update.
|
||||
* @param strings The list of strings to be added to the string file.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws CoreException if the method fails to update the project.
|
||||
* @throws IOException if the method fails to create the files in the
|
||||
* project.
|
||||
*/
|
||||
private void addStringDictionaryFile(IProject project,
|
||||
Map<String, String> strings, IProgressMonitor monitor)
|
||||
throws CoreException, IOException {
|
||||
|
||||
// create the IFile object and check if the file doesn't already exist.
|
||||
IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
|
||||
+ VALUES_DIRECTORY + AndroidConstants.WS_SEP + STRINGS_FILE);
|
||||
if (!file.exists()) {
|
||||
// get the Strings.xml template
|
||||
String stringDefinitionTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_STRINGS);
|
||||
|
||||
// get the template for one string
|
||||
String stringTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_STRING);
|
||||
|
||||
// get all the string names
|
||||
Set<String> stringNames = strings.keySet();
|
||||
|
||||
// loop on it and create the string definitions
|
||||
StringBuilder stringNodes = new StringBuilder();
|
||||
for (String key : stringNames) {
|
||||
// get the value from the key
|
||||
String value = strings.get(key);
|
||||
|
||||
// place them in the template
|
||||
String stringDef = stringTemplate.replace(PARAM_STRING_NAME, key);
|
||||
stringDef = stringDef.replace(PARAM_STRING_CONTENT, value);
|
||||
|
||||
// append to the other string
|
||||
if (stringNodes.length() > 0) {
|
||||
stringNodes.append("\n");
|
||||
}
|
||||
stringNodes.append(stringDef);
|
||||
}
|
||||
|
||||
// put the string nodes in the Strings.xml template
|
||||
stringDefinitionTemplate = stringDefinitionTemplate.replace(PH_STRINGS,
|
||||
stringNodes.toString());
|
||||
|
||||
// write the file as UTF-8
|
||||
InputStream stream = new ByteArrayInputStream(
|
||||
stringDefinitionTemplate.getBytes("UTF-8")); //$NON-NLS-1$
|
||||
file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds default application icon to the project.
|
||||
*
|
||||
* @param project The Java Project to update.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws CoreException if the method fails to update the project.
|
||||
*/
|
||||
private void addIcon(IProject project, IProgressMonitor monitor)
|
||||
throws CoreException {
|
||||
IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP
|
||||
+ DRAWABLE_DIRECTORY + AndroidConstants.WS_SEP + ICON);
|
||||
if (!file.exists()) {
|
||||
// read the content from the template
|
||||
byte[] buffer = AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON);
|
||||
|
||||
// if valid
|
||||
if (buffer != null) {
|
||||
// Save in the project
|
||||
InputStream stream = new ByteArrayInputStream(buffer);
|
||||
file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the package folder and copies the sample code in the project.
|
||||
*
|
||||
* @param project The Java Project to update.
|
||||
* @param parameters Template Parameters.
|
||||
* @param stringDictionary String List to be added to a string definition
|
||||
* file. This map will be filled by this method.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws CoreException if the method fails to update the project.
|
||||
* @throws IOException if the method fails to create the files in the
|
||||
* project.
|
||||
*/
|
||||
private void addSampleCode(IProject project, String sourceFolder,
|
||||
Map<String, String> 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);
|
||||
|
||||
// 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;
|
||||
if (activityName != null) {
|
||||
if (activityName.indexOf('.') >= 0) {
|
||||
// There are package names in the activity name. Transform packageName to add
|
||||
// those sub packages and remove them from activityName.
|
||||
packageName += "." + activityName; //$NON-NLS-1$
|
||||
int pos = packageName.lastIndexOf('.');
|
||||
activityName = packageName.substring(pos + 1);
|
||||
packageName = packageName.substring(0, pos);
|
||||
|
||||
// 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.put(PARAM_PACKAGE, packageName);
|
||||
java_activity_parameters.put(PARAM_ACTIVITY, activityName);
|
||||
}
|
||||
}
|
||||
|
||||
String[] components = packageName.split(AndroidConstants.RE_DOT);
|
||||
for (String component : components) {
|
||||
pkgFolder = pkgFolder.getFolder(component);
|
||||
if (!pkgFolder.exists()) {
|
||||
pkgFolder.create(true /* force */, true /* local */,
|
||||
new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
}
|
||||
|
||||
if (activityName != null) {
|
||||
// create the main activity Java file
|
||||
String activityJava = activityName + AndroidConstants.DOT_JAVA;
|
||||
IFile file = pkgFolder.getFile(activityJava);
|
||||
if (!file.exists()) {
|
||||
copyFile(JAVA_ACTIVITY_TEMPLATE, file, java_activity_parameters, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
// create the layout file
|
||||
IFolder layoutfolder = project.getFolder(RES_DIRECTORY).getFolder(LAYOUT_DIRECTORY);
|
||||
IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML);
|
||||
if (!file.exists()) {
|
||||
copyFile(LAYOUT_TEMPLATE, file, parameters, monitor);
|
||||
if (activityName != null) {
|
||||
stringDictionary.put(STRING_HELLO_WORLD, "Hello World, " + activityName + "!");
|
||||
} else {
|
||||
stringDictionary.put(STRING_HELLO_WORLD, "Hello World!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given folder to the project's class path.
|
||||
*
|
||||
* @param javaProject The Java Project to update.
|
||||
* @param sourceFolder Template Parameters.
|
||||
* @param monitor An existing monitor.
|
||||
* @throws JavaModelException if the classpath could not be set.
|
||||
*/
|
||||
private void setupSourceFolder(IJavaProject javaProject, String sourceFolder,
|
||||
IProgressMonitor monitor) throws JavaModelException {
|
||||
IProject project = javaProject.getProject();
|
||||
|
||||
// Add "src" to class path
|
||||
IFolder srcFolder = project.getFolder(sourceFolder);
|
||||
|
||||
IClasspathEntry[] entries = javaProject.getRawClasspath();
|
||||
entries = removeSourceClasspath(entries, srcFolder);
|
||||
entries = removeSourceClasspath(entries, srcFolder.getParent());
|
||||
|
||||
entries = ProjectHelper.addEntryToClasspath(entries,
|
||||
JavaCore.newSourceEntry(srcFolder.getFullPath()));
|
||||
|
||||
javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the corresponding source folder from the class path entries if
|
||||
* found.
|
||||
*
|
||||
* @param entries The class path entries to read. A copy will be returned.
|
||||
* @param folder The parent source folder to remove.
|
||||
* @return A new class path entries array.
|
||||
*/
|
||||
private IClasspathEntry[] removeSourceClasspath(IClasspathEntry[] entries, IContainer folder) {
|
||||
if (folder == null) {
|
||||
return entries;
|
||||
}
|
||||
IClasspathEntry source = JavaCore.newSourceEntry(folder.getFullPath());
|
||||
int n = entries.length;
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
if (entries[i].equals(source)) {
|
||||
IClasspathEntry[] newEntries = new IClasspathEntry[n - 1];
|
||||
if (i > 0) System.arraycopy(entries, 0, newEntries, 0, i);
|
||||
if (i < n - 1) System.arraycopy(entries, i + 1, newEntries, i, n - i - 1);
|
||||
n--;
|
||||
entries = newEntries;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies the given file from our resource folder to the new project.
|
||||
* Expects the file to the US-ASCII or UTF-8 encoded.
|
||||
*
|
||||
* @throws CoreException from IFile if failing to create the new file.
|
||||
* @throws MalformedURLException from URL if failing to interpret the URL.
|
||||
* @throws FileNotFoundException from RandomAccessFile.
|
||||
* @throws IOException from RandomAccessFile.length() if can't determine the
|
||||
* length.
|
||||
*/
|
||||
private void copyFile(String resourceFilename, IFile destFile,
|
||||
Map<String, String> parameters, IProgressMonitor monitor)
|
||||
throws CoreException, IOException {
|
||||
|
||||
// Read existing file.
|
||||
String template = AdtPlugin.readEmbeddedTextFile(
|
||||
TEMPLATES_DIRECTORY + resourceFilename);
|
||||
|
||||
// Replace all keyword parameters
|
||||
template = replaceParameters(template, parameters);
|
||||
|
||||
// Save in the project as UTF-8
|
||||
InputStream stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$
|
||||
destFile.create(stream, false /* force */, new SubProgressMonitor(monitor, 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image descriptor for the wizard logo.
|
||||
*/
|
||||
private void setImageDescriptor() {
|
||||
ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
|
||||
setDefaultPageImageDescriptor(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
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 final static class ClassWrapper implements IClass {
|
||||
private Class<?> mClass;
|
||||
|
||||
public ClassWrapper(Class<?> clazz) {
|
||||
mClass = clazz;
|
||||
}
|
||||
|
||||
public String getCanonicalName() {
|
||||
return mClass.getCanonicalName();
|
||||
}
|
||||
|
||||
public IClass[] getDeclaredClasses() {
|
||||
Class<?>[] classes = mClass.getDeclaredClasses();
|
||||
IClass[] iclasses = new IClass[classes.length];
|
||||
for (int i = 0 ; i < classes.length ; i++) {
|
||||
iclasses[i] = new ClassWrapper(classes[i]);
|
||||
}
|
||||
|
||||
return iclasses;
|
||||
}
|
||||
|
||||
public IClass getEnclosingClass() {
|
||||
return new ClassWrapper(mClass.getEnclosingClass());
|
||||
}
|
||||
|
||||
public String getSimpleName() {
|
||||
return mClass.getSimpleName();
|
||||
}
|
||||
|
||||
public IClass getSuperclass() {
|
||||
return new ClassWrapper(mClass.getSuperclass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object clazz) {
|
||||
if (clazz instanceof ClassWrapper) {
|
||||
return mClass.equals(((ClassWrapper)clazz).mClass);
|
||||
}
|
||||
return super.equals(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mClass.hashCode();
|
||||
}
|
||||
|
||||
|
||||
public boolean isInstantiable() {
|
||||
int modifiers = mClass.getModifiers();
|
||||
return Modifier.isAbstract(modifiers) == false && Modifier.isPublic(modifiers) == true;
|
||||
}
|
||||
|
||||
public Class<?> wrappedClass() {
|
||||
return mClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String mOsFrameworkLocation;
|
||||
|
||||
/** A cache for binary data extracted from the zip */
|
||||
private final HashMap<String, byte[]> mEntryCache = new HashMap<String, byte[]>();
|
||||
/** A cache for already defined Classes */
|
||||
private final HashMap<String, Class<?> > mClassCache = new HashMap<String, Class<?> >();
|
||||
|
||||
/**
|
||||
* Creates the class loader by providing the os path to the framework jar archive
|
||||
*
|
||||
* @param osFrameworkLocation OS Path of the framework JAR file
|
||||
*/
|
||||
public AndroidJarLoader(String osFrameworkLocation) {
|
||||
super();
|
||||
mOsFrameworkLocation = osFrameworkLocation;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return mOsFrameworkLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-loads all class binary data that belong to the given package by reading the archive
|
||||
* once and caching them internally.
|
||||
* <p/>
|
||||
* This does not actually preload "classes", it just reads the unzipped bytes for a given
|
||||
* class. To obtain a class, one must call {@link #findClass(String)} later.
|
||||
* <p/>
|
||||
* All classes which package name starts with "packageFilter" will be included and can be
|
||||
* found later.
|
||||
* <p/>
|
||||
* May throw some exceptions if the framework JAR cannot be read.
|
||||
*
|
||||
* @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 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)
|
||||
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);
|
||||
|
||||
// create streams to read the intermediary archive
|
||||
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
|
||||
ZipInputStream zis = new ZipInputStream(fis);
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
// get the name of the entry.
|
||||
String entryPath = entry.getName();
|
||||
|
||||
if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
|
||||
// only accept class files
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if it is part of the package to preload
|
||||
if (pathFilter.length() > 0 && !entryPath.startsWith(pathFilter)) {
|
||||
continue;
|
||||
}
|
||||
String className = entryPathToClassName(entryPath);
|
||||
|
||||
if (!mEntryCache.containsKey(className)) {
|
||||
long entrySize = entry.getSize();
|
||||
if (entrySize > Integer.MAX_VALUE) {
|
||||
throw new InvalidAttributeValueException();
|
||||
}
|
||||
byte[] data = readZipData(zis, (int)entrySize);
|
||||
mEntryCache.put(className, data);
|
||||
}
|
||||
|
||||
// advance 5% of whatever is allocated on the progress bar
|
||||
progress.setWorkRemaining(100);
|
||||
progress.worked(5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and loads all classes that derive from a given set of super classes.
|
||||
* <p/>
|
||||
* As a side-effect this will load and cache most, if not all, classes in the input JAR file.
|
||||
*
|
||||
* @param packageFilter Base name of package of classes to find.
|
||||
* Use an empty string to find everyting.
|
||||
* @param superClasses The super classes of all the classes to find.
|
||||
* @return An hash map which keys are the super classes looked for and which values are
|
||||
* ArrayList of the classes found. The array lists are always created for all the
|
||||
* valid keys, they are simply empty if no deriving class is found for a given
|
||||
* super class.
|
||||
* @throws IOException
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> 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>>();
|
||||
|
||||
for (String className : superClasses) {
|
||||
mClassesFound.put(className, new ArrayList<IClass>());
|
||||
}
|
||||
|
||||
// create streams to read the intermediary archive
|
||||
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
|
||||
ZipInputStream zis = new ZipInputStream(fis);
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
// get the name of the entry and convert to a class binary name
|
||||
String entryPath = entry.getName();
|
||||
if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
|
||||
// only accept class files
|
||||
continue;
|
||||
}
|
||||
if (packageFilter.length() > 0 && !entryPath.startsWith(packageFilter)) {
|
||||
// only accept stuff from the requested root package.
|
||||
continue;
|
||||
}
|
||||
String className = entryPathToClassName(entryPath);
|
||||
|
||||
Class<?> loaded_class = mClassCache.get(className);
|
||||
if (loaded_class == null) {
|
||||
byte[] data = mEntryCache.get(className);
|
||||
if (data == null) {
|
||||
// Get the class and cache it
|
||||
long entrySize = entry.getSize();
|
||||
if (entrySize > Integer.MAX_VALUE) {
|
||||
throw new InvalidAttributeValueException();
|
||||
}
|
||||
data = readZipData(zis, (int)entrySize);
|
||||
}
|
||||
loaded_class = defineAndCacheClass(className, data);
|
||||
}
|
||||
|
||||
for (Class<?> superClass = loaded_class.getSuperclass();
|
||||
superClass != null;
|
||||
superClass = superClass.getSuperclass()) {
|
||||
String superName = superClass.getCanonicalName();
|
||||
if (mClassesFound.containsKey(superName)) {
|
||||
mClassesFound.get(superName).add(new ClassWrapper(loaded_class));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mClassesFound;
|
||||
}
|
||||
|
||||
/** Helper method that converts a Zip entry path into a corresponding
|
||||
* Java full qualified binary class name.
|
||||
* <p/>
|
||||
* F.ex, this converts "com/my/package/Foo.class" into "com.my.package.Foo".
|
||||
*/
|
||||
private String entryPathToClassName(String entryPath) {
|
||||
return entryPath.replaceFirst("\\.class$", "").replaceAll("[/\\\\]", "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the class with the specified binary name.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
// try to find the class in the cache
|
||||
Class<?> cached_class = mClassCache.get(name);
|
||||
if (cached_class == ClassNotFoundException.class) {
|
||||
// we already know we can't find this class, don't try again
|
||||
throw new ClassNotFoundException(name);
|
||||
} else if (cached_class != null) {
|
||||
return cached_class;
|
||||
}
|
||||
|
||||
// if not found, look it up and cache it
|
||||
byte[] data = loadClassData(name);
|
||||
if (data != null) {
|
||||
return defineAndCacheClass(name, data);
|
||||
} else {
|
||||
// if the class can't be found, record a CNFE class in the map so
|
||||
// that we don't try to reload it next time
|
||||
mClassCache.put(name, ClassNotFoundException.class);
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ClassNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a class based on its binary data and caches the resulting class object.
|
||||
*
|
||||
* @param name The binary name of the class (i.e. package.class1$class2)
|
||||
* @param data The binary data from the loader.
|
||||
* @return The class defined
|
||||
* @throws ClassFormatError if defineClass failed.
|
||||
*/
|
||||
private Class<?> defineAndCacheClass(String name, byte[] data) throws ClassFormatError {
|
||||
Class<?> cached_class;
|
||||
cached_class = defineClass(null, data, 0, data.length);
|
||||
|
||||
if (cached_class != null) {
|
||||
// Add new class to the cache class and remove it from the zip entry data cache
|
||||
mClassCache.put(name, cached_class);
|
||||
mEntryCache.remove(name);
|
||||
}
|
||||
return cached_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a class data from its binary name.
|
||||
* <p/>
|
||||
* This uses the class binary data that has been preloaded earlier by the preLoadClasses()
|
||||
* method if possible.
|
||||
*
|
||||
* @param className the binary name
|
||||
* @return an array of bytes representing the class data or null if not found
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized byte[] loadClassData(String className)
|
||||
throws InvalidAttributeValueException, IOException {
|
||||
|
||||
byte[] data = mEntryCache.get(className);
|
||||
if (data != null) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// The name is a binary name. Something like "android.R", or "android.R$id".
|
||||
// Make a path out of it.
|
||||
String entryName = className.replaceAll("\\.", "/") + AndroidConstants.DOT_CLASS; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// create streams to read the intermediary archive
|
||||
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
|
||||
ZipInputStream zis = new ZipInputStream(fis);
|
||||
|
||||
// loop on the entries of the intermediary package and put them in the final package.
|
||||
ZipEntry entry;
|
||||
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
// get the name of the entry.
|
||||
String currEntryName = entry.getName();
|
||||
|
||||
if (currEntryName.equals(entryName)) {
|
||||
long entrySize = entry.getSize();
|
||||
if (entrySize > Integer.MAX_VALUE) {
|
||||
throw new InvalidAttributeValueException();
|
||||
}
|
||||
|
||||
data = readZipData(zis, (int)entrySize);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data for the <em>current</em> entry from the zip input stream.
|
||||
*
|
||||
* @param zis The Zip input stream
|
||||
* @param entrySize The entry size. -1 if unknown.
|
||||
* @return The new data for the <em>current</em> entry.
|
||||
* @throws IOException If ZipInputStream.read() fails.
|
||||
*/
|
||||
private byte[] readZipData(ZipInputStream zis, int entrySize) throws IOException {
|
||||
int block_size = 1024;
|
||||
int data_size = entrySize < 1 ? block_size : entrySize;
|
||||
int offset = 0;
|
||||
byte[] data = new byte[data_size];
|
||||
|
||||
while(zis.available() != 0) {
|
||||
int count = zis.read(data, offset, data_size - offset);
|
||||
if (count < 0) { // read data is done
|
||||
break;
|
||||
}
|
||||
offset += count;
|
||||
|
||||
if (entrySize >= 1 && offset >= entrySize) { // we know the size and we're done
|
||||
break;
|
||||
}
|
||||
|
||||
// if we don't know the entry size and we're not done reading,
|
||||
// expand the data buffer some more.
|
||||
if (offset >= data_size) {
|
||||
byte[] temp = new byte[data_size + block_size];
|
||||
System.arraycopy(data, 0, temp, 0, data_size);
|
||||
data_size += block_size;
|
||||
data = temp;
|
||||
block_size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset < data_size) {
|
||||
// buffer was allocated too large, trim it
|
||||
byte[] temp = new byte[offset];
|
||||
if (offset > 0) {
|
||||
System.arraycopy(data, 0, temp, 0, offset);
|
||||
}
|
||||
data = temp;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link IClass} by its fully-qualified name.
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException {
|
||||
try {
|
||||
return new ClassWrapper(loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw e; // useful for debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
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.ResourceItem;
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
|
||||
/**
|
||||
* Parser for the framework library.
|
||||
* <p/>
|
||||
* This gather the following information:
|
||||
* <ul>
|
||||
* <li>Resource ID from <code>android.R</code></li>
|
||||
* <li>The list of permissions values from <code>android.Manifest$permission</code></li>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class FrameworkResourceParser {
|
||||
|
||||
private static final String TAG = "Framework Resource Parser";
|
||||
|
||||
/**
|
||||
* Creates a framework resource parser.
|
||||
*/
|
||||
public FrameworkResourceParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the framework, collects all interesting information and stores them in the
|
||||
* {@link FrameworkResourceManager} given to the constructor.
|
||||
*
|
||||
* @param osSdkPath the OS path of the SDK directory.
|
||||
* @param resourceManager the {@link FrameworkResourceManager} that will store the parsed
|
||||
* resources.
|
||||
* @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;
|
||||
}
|
||||
|
||||
try {
|
||||
SubMonitor progress = SubMonitor.convert(monitor, 100);
|
||||
|
||||
AndroidJarLoader classLoader =
|
||||
new AndroidJarLoader(osSdkPath + AndroidConstants.FN_FRAMEWORK_LIBRARY);
|
||||
|
||||
progress.subTask("Preloading");
|
||||
preload(classLoader, progress.newChild(40));
|
||||
progress.setWorkRemaining(60);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the resource Ids.
|
||||
progress.subTask("Resource IDs");
|
||||
FrameworkResourceRepository systemResourceRepository = new FrameworkResourceRepository(
|
||||
collectResourceIds(classLoader));
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the permissions
|
||||
progress.subTask("Permissions");
|
||||
String[] permissionValues = collectPermissions(classLoader);
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
progress.worked(5);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.subTask("Layouts");
|
||||
AttrsXmlParser attrsXmlParser = new AttrsXmlParser(
|
||||
osSdkPath + AndroidConstants.OS_SDK_ATTRS_XML);
|
||||
attrsXmlParser.preload();
|
||||
|
||||
AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser(
|
||||
osSdkPath + AndroidConstants.OS_SDK_ATTRS_MANIFEST_XML,
|
||||
attrsXmlParser);
|
||||
attrsManifestXmlParser.preload();
|
||||
|
||||
Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>();
|
||||
Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>();
|
||||
|
||||
collectLayoutClasses(osLibPath, classLoader, attrsXmlParser, mainList, groupList,
|
||||
progress.newChild(40));
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]);
|
||||
ViewClassInfo[] layoutGroupsInfo = groupList.toArray(
|
||||
new ViewClassInfo[groupList.size()]);
|
||||
|
||||
mainList.clear();
|
||||
groupList.clear();
|
||||
collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList,
|
||||
progress.newChild(5));
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]);
|
||||
ViewClassInfo[] preferenceGroupsInfo = groupList.toArray(
|
||||
new ViewClassInfo[groupList.size()]);
|
||||
|
||||
Map<String, DeclareStyleableInfo> xmlMenuMap = collectMenuDefinitions(attrsXmlParser);
|
||||
Map<String, DeclareStyleableInfo> xmlSearchableMap = collectSearchableDefinitions(
|
||||
attrsXmlParser);
|
||||
Map<String, DeclareStyleableInfo> manifestMap = collectManifestDefinitions(
|
||||
attrsManifestXmlParser);
|
||||
Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues();
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String docBaseUrl = getDocumentationBaseUrl(
|
||||
osSdkPath + AndroidConstants.OS_SDK_DOCS_FOLDER);
|
||||
|
||||
FrameworkResourceManager.getInstance().setResources(systemResourceRepository,
|
||||
layoutViewsInfo,
|
||||
layoutGroupsInfo,
|
||||
preferencesInfo,
|
||||
preferenceGroupsInfo,
|
||||
xmlMenuMap,
|
||||
xmlSearchableMap,
|
||||
manifestMap,
|
||||
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);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads all "interesting" classes from the framework SDK jar.
|
||||
* <p/>
|
||||
* Currently this preloads all classes from the framework jar
|
||||
*
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
|
||||
*/
|
||||
private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) {
|
||||
try {
|
||||
classLoader.preLoadClasses("" /* all classes */, monitor); //$NON-NLS-1$
|
||||
} catch (InvalidAttributeValueException e) {
|
||||
AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the resources IDs found in the SDK.
|
||||
*
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @return a map of the resources, or null if it failed.
|
||||
*/
|
||||
private Map<ResourceType, List<ResourceItem>> collectResourceIds(
|
||||
AndroidJarLoader classLoader) {
|
||||
try {
|
||||
Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R);
|
||||
|
||||
if (r != null) {
|
||||
return parseRClass(r);
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the R class and build the resource map.
|
||||
*
|
||||
* @param rClass the Class object representing the Resources.
|
||||
* @return a map of the resource or null
|
||||
*/
|
||||
private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) {
|
||||
// get the sub classes.
|
||||
Class<?>[] classes = rClass.getClasses();
|
||||
|
||||
if (classes.length > 0) {
|
||||
HashMap<ResourceType, List<ResourceItem>> map =
|
||||
new HashMap<ResourceType, List<ResourceItem>>();
|
||||
|
||||
// get the fields of each class.
|
||||
for (int c = 0 ; c < classes.length ; c++) {
|
||||
Class<?> subClass = classes[c];
|
||||
String name = subClass.getSimpleName();
|
||||
|
||||
// get the matching ResourceType
|
||||
ResourceType type = ResourceType.getEnum(name);
|
||||
if (type != null) {
|
||||
List<ResourceItem> list = new ArrayList<ResourceItem>();
|
||||
map.put(type, list);
|
||||
|
||||
Field[] fields = subClass.getFields();
|
||||
|
||||
for (Field f : fields) {
|
||||
list.add(new ResourceItem(f.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads, collects and returns the list of default permissions from the framework.
|
||||
*
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @return a non null (but possibly empty) array containing the permission values.
|
||||
*/
|
||||
private String[] collectPermissions(AndroidJarLoader classLoader) {
|
||||
try {
|
||||
Class<?> permissionClass =
|
||||
classLoader.loadClass(AndroidConstants.CLASS_MANIFEST_PERMISSION);
|
||||
|
||||
if (permissionClass != null) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
Field[] fields = permissionClass.getFields();
|
||||
|
||||
for (Field f : fields) {
|
||||
int modifiers = f.getModifiers();
|
||||
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) &&
|
||||
Modifier.isPublic(modifiers)) {
|
||||
try {
|
||||
Object value = f.get(null);
|
||||
if (value instanceof String) {
|
||||
list.add((String)value);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// since we provide null this should not happen
|
||||
} catch (IllegalAccessException e) {
|
||||
// if the field is inaccessible we ignore it.
|
||||
} catch (NullPointerException npe) {
|
||||
// looks like this is not a static field. we can ignore.
|
||||
} catch (ExceptionInInitializerError eiie) {
|
||||
// lets just ignore the field again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG,
|
||||
"Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$
|
||||
AndroidConstants.CLASS_MANIFEST_PERMISSION,
|
||||
classLoader.getSource());
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and collects the action and category default values from the framework.
|
||||
* The values are added to the <code>actions</code> and <code>categories</code> lists.
|
||||
*
|
||||
* @param osLibPath The OS path to the SDK tools/lib folder, ending with a separator.
|
||||
* @param activityActions the list which will receive the activity action values.
|
||||
* @param broadcastActions the list which will receive the broadcast action values.
|
||||
* @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,
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects values from a text file located in the SDK
|
||||
* @param osFilePath The path to the text file.
|
||||
* @param values the {@link ArrayList} to fill with the values.
|
||||
*/
|
||||
private void collectValues(String osFilePath, ArrayList<String> values) {
|
||||
FileReader fr = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
fr = new FileReader(osFilePath);
|
||||
reader = new BufferedReader(fr);
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() > 0 && line.startsWith("#") == false) { //$NON-NLS-1$
|
||||
values.add(line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
try {
|
||||
if (fr != null) {
|
||||
fr.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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,
|
||||
AttrsXmlParser attrsXmlParser,
|
||||
Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList,
|
||||
IProgressMonitor monitor) {
|
||||
LayoutParamsParser ldp = null;
|
||||
try {
|
||||
WidgetListLoader loader = new WidgetListLoader(osLibPath + "widgets.txt");
|
||||
if (loader.parseWidgetList(monitor)) {
|
||||
ldp = new LayoutParamsParser(loader, attrsXmlParser);
|
||||
}
|
||||
// if the parsing failed, we'll use the old loader below.
|
||||
} catch (FileNotFoundException e) {
|
||||
AdtPlugin.log(e, "Android Framework Parser"); //$NON-NLS-1$
|
||||
// the file does not exist, we'll use the old loader below.
|
||||
}
|
||||
|
||||
if (ldp == null) {
|
||||
ldp = new LayoutParamsParser(classLoader, attrsXmlParser);
|
||||
}
|
||||
ldp.parseLayoutClasses(monitor);
|
||||
|
||||
List<ViewClassInfo> views = ldp.getViews();
|
||||
List<ViewClassInfo> groups = ldp.getGroups();
|
||||
|
||||
if (views != null && groups != null) {
|
||||
mainList.addAll(views);
|
||||
groupList.addAll(groups);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all preferences definition information from the attrs.xml and
|
||||
* sets the corresponding structures in the resource manager.
|
||||
*
|
||||
* @param classLoader The framework SDK jar classloader
|
||||
* @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 collectPreferenceClasses(AndroidJarLoader classLoader,
|
||||
AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList,
|
||||
Collection<ViewClassInfo> groupList, IProgressMonitor monitor) {
|
||||
LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser);
|
||||
|
||||
try {
|
||||
ldp.parsePreferencesClasses(monitor);
|
||||
|
||||
List<ViewClassInfo> prefs = ldp.getViews();
|
||||
List<ViewClassInfo> groups = ldp.getGroups();
|
||||
|
||||
if (prefs != null && groups != null) {
|
||||
mainList.addAll(prefs);
|
||||
groupList.addAll(groups);
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
AdtPlugin.logAndPrintError(e, TAG,
|
||||
"Collect preferences failed, class %1$s not found in %2$s", //$NON-NLS-1$
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all menu definition information from the attrs.xml and returns it.
|
||||
*
|
||||
* @param attrsXmlParser The parser of the attrs.xml file
|
||||
*/
|
||||
private Map<String, DeclareStyleableInfo> collectMenuDefinitions(
|
||||
AttrsXmlParser attrsXmlParser) {
|
||||
Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList();
|
||||
Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>();
|
||||
for (String key : new String[] { "Menu", //$NON-NLS-1$
|
||||
"MenuItem", //$NON-NLS-1$
|
||||
"MenuGroup" }) { //$NON-NLS-1$
|
||||
if (map.containsKey(key)) {
|
||||
map2.put(key, map.get(key));
|
||||
} else {
|
||||
AdtPlugin.log(IStatus.WARNING,
|
||||
"Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
|
||||
key, attrsXmlParser.getOsAttrsXmlPath());
|
||||
AdtPlugin.printErrorToConsole("Android Framework Parser",
|
||||
String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
|
||||
key, attrsXmlParser.getOsAttrsXmlPath()));
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(map2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all searchable definition information from the attrs.xml and returns it.
|
||||
*
|
||||
* @param attrsXmlParser The parser of the attrs.xml file
|
||||
*/
|
||||
private Map<String, DeclareStyleableInfo> collectSearchableDefinitions(
|
||||
AttrsXmlParser attrsXmlParser) {
|
||||
Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList();
|
||||
Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>();
|
||||
for (String key : new String[] { "Searchable", //$NON-NLS-1$
|
||||
"SearchableActionKey" }) { //$NON-NLS-1$
|
||||
if (map.containsKey(key)) {
|
||||
map2.put(key, map.get(key));
|
||||
} else {
|
||||
AdtPlugin.log(IStatus.WARNING,
|
||||
"Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
|
||||
key, attrsXmlParser.getOsAttrsXmlPath());
|
||||
AdtPlugin.printErrorToConsole("Android Framework Parser",
|
||||
String.format("Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
|
||||
key, attrsXmlParser.getOsAttrsXmlPath()));
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(map2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all manifest definition information from the attrs_manifest.xml and returns it.
|
||||
*/
|
||||
private Map<String, DeclareStyleableInfo> collectManifestDefinitions(
|
||||
AttrsXmlParser attrsXmlParser) {
|
||||
|
||||
return attrsXmlParser.getDeclareStyleableList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
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 java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link IResourceRepository} interface to hold the system resource Ids
|
||||
* parsed by {@link FrameworkResourceParser}.
|
||||
*/
|
||||
public final class FrameworkResourceRepository implements IResourceRepository {
|
||||
|
||||
private Map<ResourceType, List<ResourceItem>> mResourcesMap;
|
||||
|
||||
public FrameworkResourceRepository(Map<ResourceType, List<ResourceItem>> systemResourcesMap) {
|
||||
mResourcesMap = systemResourcesMap;
|
||||
}
|
||||
|
||||
public ResourceType[] getAvailableResourceTypes() {
|
||||
if (mResourcesMap != null) {
|
||||
Set<ResourceType> types = mResourcesMap.keySet();
|
||||
|
||||
if (types != null) {
|
||||
return types.toArray(new ResourceType[types.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ResourceItem[] getResources(ResourceType type) {
|
||||
if (mResourcesMap != null) {
|
||||
List<ResourceItem> items = mResourcesMap.get(type);
|
||||
|
||||
if (items != null) {
|
||||
return items.toArray(new ResourceItem[items.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasResources(ResourceType type) {
|
||||
if (mResourcesMap != null) {
|
||||
List<ResourceItem> items = mResourcesMap.get(type);
|
||||
|
||||
return (items != null && items.size() > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSystemRepository() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
|
||||
/**
|
||||
* Classes which implements this interface provide methods to access framework resource
|
||||
* data loaded from the SDK.
|
||||
*/
|
||||
public interface IAndroidLoader {
|
||||
|
||||
/**
|
||||
* Finds and loads all classes that derive from a given set of super classes.
|
||||
*
|
||||
* @param rootPackage Root package of classes to find. Use an empty string to find everyting.
|
||||
* @param superClasses The super classes of all the classes to find.
|
||||
* @return An hash map which keys are the super classes looked for and which values are
|
||||
* ArrayList of the classes found. The array lists are always created for all the
|
||||
* valid keys, they are simply empty if no deriving class is found for a given
|
||||
* super class.
|
||||
* @throws IOException
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(
|
||||
String rootPackage, String[] superClasses)
|
||||
throws IOException, InvalidAttributeValueException, ClassFormatError;
|
||||
|
||||
/**
|
||||
* Returns a {@link IClass} by its fully-qualified name.
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* Returns a string indicating the source of the classes, typically for debugging
|
||||
* or in error messages. This would typically be a JAR file name or some kind of
|
||||
* identifier that would mean something to the user when looking at error messages.
|
||||
*
|
||||
* @return An informal string representing the source of the classes.
|
||||
*/
|
||||
public String getSource();
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
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;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
|
||||
/*
|
||||
* TODO: refactor this. Could use some cleanup.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser for the framework library.
|
||||
* <p/>
|
||||
* This gather the following information:
|
||||
* <ul>
|
||||
* <li>Resource ID from <code>android.R</code></li>
|
||||
* <li>The list of permissions values from <code>android.Manifest$permission</code></li>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class LayoutParamsParser {
|
||||
|
||||
/**
|
||||
* Classes which implement this interface provide methods to describe a class.
|
||||
*/
|
||||
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;
|
||||
|
||||
ExtViewClassInfo(boolean instantiable, boolean isLayout, String canonicalClassName,
|
||||
String shortClassName) {
|
||||
super(isLayout, canonicalClassName, shortClassName);
|
||||
mIsInstantiable = instantiable;
|
||||
}
|
||||
|
||||
boolean isInstantiable() {
|
||||
return mIsInstantiable;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: protected members/methods are overridden in unit tests */
|
||||
|
||||
/** Reference to android.view.View */
|
||||
protected IClass mTopViewClass;
|
||||
/** Reference to android.view.ViewGroup */
|
||||
protected IClass mTopGroupClass;
|
||||
/** Reference to android.view.ViewGroup$LayoutParams */
|
||||
protected IClass mTopLayoutParamsClass;
|
||||
|
||||
/** Input list of all classes deriving from android.view.View */
|
||||
protected ArrayList<IClass> mViewList;
|
||||
/** Input list of all classes deriving from android.view.ViewGroup */
|
||||
protected ArrayList<IClass> mGroupList;
|
||||
|
||||
/** Output map of FQCN => info on View classes */
|
||||
protected TreeMap<String, ExtViewClassInfo> mViewMap;
|
||||
/** Output map of FQCN => info on ViewGroup classes */
|
||||
protected TreeMap<String, ExtViewClassInfo> mGroupMap;
|
||||
/** Output map of FQCN => info on LayoutParams classes */
|
||||
protected HashMap<String, LayoutParamsInfo> mLayoutParamsMap;
|
||||
|
||||
/** The attrs.xml parser */
|
||||
protected AttrsXmlParser mAttrsXmlParser;
|
||||
|
||||
/** The android.jar class loader */
|
||||
protected IAndroidLoader 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,
|
||||
AttrsXmlParser attrsXmlParser) {
|
||||
mClassLoader = classLoader;
|
||||
mAttrsXmlParser = attrsXmlParser;
|
||||
}
|
||||
|
||||
/** Returns the map of FQCN => info on View classes */
|
||||
public List<ViewClassInfo> getViews() {
|
||||
return getInstantiables(mViewMap);
|
||||
}
|
||||
|
||||
/** Returns the map of FQCN => info on ViewGroup classes */
|
||||
public List<ViewClassInfo> getGroups() {
|
||||
return getInstantiables(mGroupMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <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.
|
||||
*/
|
||||
public void parseLayoutClasses(IProgressMonitor monitor) {
|
||||
parseClasses(monitor,
|
||||
AndroidConstants.CLASS_VIEW,
|
||||
AndroidConstants.CLASS_VIEWGROUP,
|
||||
AndroidConstants.CLASS_VIEWGROUP_LAYOUTPARAMS);
|
||||
}
|
||||
|
||||
public void parsePreferencesClasses(IProgressMonitor monitor) {
|
||||
parseClasses(monitor,
|
||||
AndroidConstants.CLASS_PREFERENCE,
|
||||
AndroidConstants.CLASS_PREFERENCEGROUP,
|
||||
null /* paramsClassName */ );
|
||||
}
|
||||
|
||||
private void parseClasses(IProgressMonitor monitor,
|
||||
String rootClassName,
|
||||
String groupClassName,
|
||||
String paramsClassName) {
|
||||
try {
|
||||
SubMonitor progress = SubMonitor.convert(monitor, 100);
|
||||
|
||||
String[] superClasses = new String[2 + (paramsClassName == null ? 0 : 1)];
|
||||
superClasses[0] = groupClassName;
|
||||
superClasses[1] = rootClassName;
|
||||
if (paramsClassName != null) {
|
||||
superClasses[2] = paramsClassName;
|
||||
}
|
||||
HashMap<String, ArrayList<IClass>> found = mClassLoader.findClassesDerivingFrom(
|
||||
"android.", superClasses);
|
||||
mTopViewClass = mClassLoader.getClass(rootClassName);
|
||||
mTopGroupClass = mClassLoader.getClass(groupClassName);
|
||||
if (paramsClassName != null) {
|
||||
mTopLayoutParamsClass = mClassLoader.getClass(paramsClassName);
|
||||
}
|
||||
|
||||
mViewList = found.get(rootClassName);
|
||||
mGroupList = found.get(groupClassName);
|
||||
|
||||
mViewMap = new TreeMap<String, ExtViewClassInfo>();
|
||||
mGroupMap = new TreeMap<String, ExtViewClassInfo>();
|
||||
if (mTopLayoutParamsClass != null) {
|
||||
mLayoutParamsMap = new HashMap<String, LayoutParamsInfo>();
|
||||
}
|
||||
|
||||
// Add top classes to the maps since by design they are not listed in classes deriving
|
||||
// from themselves.
|
||||
addGroup(mTopGroupClass);
|
||||
addView(mTopViewClass);
|
||||
|
||||
// ViewGroup derives from View
|
||||
mGroupMap.get(groupClassName).setSuperClass(
|
||||
mViewMap.get(rootClassName));
|
||||
|
||||
progress.setWorkRemaining(mGroupList.size() + mViewList.size());
|
||||
|
||||
for (IClass groupChild : mGroupList) {
|
||||
addGroup(groupChild);
|
||||
progress.worked(1);
|
||||
}
|
||||
|
||||
for (IClass 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$
|
||||
rootClassName, groupClassName);
|
||||
} catch (InvalidAttributeValueException e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
} catch (ClassFormatError e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
String fqcn = viewClass.getCanonicalName();
|
||||
if (mViewMap.containsKey(fqcn)) {
|
||||
return mViewMap.get(fqcn);
|
||||
} else if (mGroupMap.containsKey(fqcn)) {
|
||||
return mGroupMap.get(fqcn);
|
||||
}
|
||||
|
||||
ExtViewClassInfo info = new ExtViewClassInfo(viewClass.isInstantiable(),
|
||||
false /* layout */, fqcn, viewClass.getSimpleName());
|
||||
mViewMap.put(fqcn, info);
|
||||
|
||||
// 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();
|
||||
ExtViewClassInfo superClassInfo = addView(superClass);
|
||||
info.setSuperClass(superClassInfo);
|
||||
}
|
||||
|
||||
mAttrsXmlParser.loadViewAttributes(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
String fqcn = groupClass.getCanonicalName();
|
||||
if (mGroupMap.containsKey(fqcn)) {
|
||||
return mGroupMap.get(fqcn);
|
||||
}
|
||||
|
||||
ExtViewClassInfo info = new ExtViewClassInfo(groupClass.isInstantiable(),
|
||||
true /* layout */, fqcn, groupClass.getSimpleName());
|
||||
mGroupMap.put(fqcn, info);
|
||||
|
||||
// All groups derive from android.view.ViewGroup, which in turns derives from
|
||||
// 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();
|
||||
|
||||
// Assertion: at this point, we should have
|
||||
// superClass != mTopViewClass || fqcn.equals(AndroidConstants.CLASS_VIEWGROUP);
|
||||
|
||||
if (superClass != null && superClass.equals(mTopViewClass) == false) {
|
||||
ExtViewClassInfo superClassInfo = addGroup(superClass);
|
||||
|
||||
// Assertion: we should have superClassInfo != null && superClassInfo != info;
|
||||
if (superClassInfo != null && superClassInfo != info) {
|
||||
info.setSuperClass(superClassInfo);
|
||||
}
|
||||
}
|
||||
|
||||
mAttrsXmlParser.loadViewAttributes(info);
|
||||
if (mTopLayoutParamsClass != null) {
|
||||
info.setLayoutParams(addLayoutParams(groupClass));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a ViewGroup class and returns an info object on its inner LayoutParams.
|
||||
*
|
||||
* @return The {@link LayoutParamsInfo} for the ViewGroup class or null.
|
||||
*/
|
||||
private LayoutParamsInfo addLayoutParams(IClass groupClass) {
|
||||
|
||||
// Is there a LayoutParams in this group class?
|
||||
IClass 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();
|
||||
layoutParamsClass == null &&
|
||||
superClass != null &&
|
||||
superClass.equals(mTopViewClass) == false;
|
||||
superClass = superClass.getSuperclass()) {
|
||||
layoutParamsClass = findLayoutParams(superClass);
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutParamsClass != null) {
|
||||
return getLayoutParamsInfo(layoutParamsClass);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
String fqcn = layoutParamsClass.getCanonicalName();
|
||||
LayoutParamsInfo layoutParamsInfo = mLayoutParamsMap.get(fqcn);
|
||||
|
||||
if (layoutParamsInfo != null) {
|
||||
return layoutParamsInfo;
|
||||
}
|
||||
|
||||
// Find the link on the LayoutParams super class
|
||||
LayoutParamsInfo superClassInfo = null;
|
||||
if (layoutParamsClass.equals(mTopLayoutParamsClass) == false) {
|
||||
IClass superClass = layoutParamsClass.getSuperclass();
|
||||
superClassInfo = getLayoutParamsInfo(superClass);
|
||||
}
|
||||
|
||||
// Find the link on the enclosing ViewGroup
|
||||
ExtViewClassInfo enclosingGroupInfo = addGroup(layoutParamsClass.getEnclosingClass());
|
||||
|
||||
layoutParamsInfo = new ExtViewClassInfo.LayoutParamsInfo(
|
||||
enclosingGroupInfo, layoutParamsClass.getSimpleName(), superClassInfo);
|
||||
mLayoutParamsMap.put(fqcn, layoutParamsInfo);
|
||||
|
||||
mAttrsXmlParser.loadLayoutParamsAttributes(layoutParamsInfo);
|
||||
|
||||
return layoutParamsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a ViewGroup-derived class, looks for an inner class named LayoutParams
|
||||
* and if found returns its class definition.
|
||||
* <p/>
|
||||
* This uses the actual defined inner classes and does not look at inherited classes.
|
||||
*
|
||||
* @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) {
|
||||
if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_LAYOUTPARAMS)) {
|
||||
return innerClass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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) {
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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.resources;
|
||||
|
||||
import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass;
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
|
||||
/**
|
||||
* Parser for the text file containing the list of widgets, layouts and layout params.
|
||||
* <p/>
|
||||
* The file is a straight text file containing one class per line.<br>
|
||||
* Each line is in the following format<br>
|
||||
* <code>[code][class name] [super class name] [super class name]...</code>
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* Basic class containing the class descriptions found in the text file.
|
||||
*/
|
||||
private final static class ClassDescriptor implements IClass {
|
||||
|
||||
private String mName;
|
||||
private String mSimpleName;
|
||||
private ClassDescriptor mSuperClass;
|
||||
private ClassDescriptor mEnclosingClass;
|
||||
private final ArrayList<IClass> mDeclaredClasses = new ArrayList<IClass>();
|
||||
private boolean mIsInstantiable = false;
|
||||
|
||||
ClassDescriptor(String fqcn) {
|
||||
mName = fqcn;
|
||||
mSimpleName = getSimpleName(fqcn);
|
||||
}
|
||||
|
||||
public String getCanonicalName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getSimpleName() {
|
||||
return mSimpleName;
|
||||
}
|
||||
|
||||
public IClass[] getDeclaredClasses() {
|
||||
return mDeclaredClasses.toArray(new IClass[mDeclaredClasses.size()]);
|
||||
}
|
||||
|
||||
private void addDeclaredClass(ClassDescriptor declaredClass) {
|
||||
mDeclaredClasses.add(declaredClass);
|
||||
}
|
||||
|
||||
public IClass getEnclosingClass() {
|
||||
return mEnclosingClass;
|
||||
}
|
||||
|
||||
void setEnclosingClass(ClassDescriptor enclosingClass) {
|
||||
// set the enclosing class.
|
||||
mEnclosingClass = enclosingClass;
|
||||
|
||||
// add this to the list of declared class in the enclosing class.
|
||||
mEnclosingClass.addDeclaredClass(this);
|
||||
|
||||
// finally change the name of declared class to make sure it uses the
|
||||
// convention: package.enclosing$declared instead of package.enclosing.declared
|
||||
mName = enclosingClass.mName + "$" + mName.substring(enclosingClass.mName.length() + 1);
|
||||
}
|
||||
|
||||
public IClass getSuperclass() {
|
||||
return mSuperClass;
|
||||
}
|
||||
|
||||
void setSuperClass(ClassDescriptor superClass) {
|
||||
mSuperClass = superClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object clazz) {
|
||||
if (clazz instanceof ClassDescriptor) {
|
||||
return mName.equals(((ClassDescriptor)clazz).mName);
|
||||
}
|
||||
return super.equals(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mName.hashCode();
|
||||
}
|
||||
|
||||
public boolean isInstantiable() {
|
||||
return mIsInstantiable;
|
||||
}
|
||||
|
||||
void setInstantiable(boolean state) {
|
||||
mIsInstantiable = state;
|
||||
}
|
||||
|
||||
private String getSimpleName(String fqcn) {
|
||||
String[] segments = fqcn.split("\\.");
|
||||
return segments[segments.length-1];
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedReader mReader;
|
||||
|
||||
/** Output map of FQCN => descriptor on all classes */
|
||||
private final Map<String, ClassDescriptor> mMap = new TreeMap<String, ClassDescriptor>();
|
||||
/** Output map of FQCN => descriptor on View classes */
|
||||
private final Map<String, ClassDescriptor> mWidgetMap = new TreeMap<String, ClassDescriptor>();
|
||||
/** Output map of FQCN => descriptor on ViewGroup classes */
|
||||
private final Map<String, ClassDescriptor> mLayoutMap = new TreeMap<String, ClassDescriptor>();
|
||||
/** Output map of FQCN => descriptor on LayoutParams classes */
|
||||
private final Map<String, ClassDescriptor> mLayoutParamsMap =
|
||||
new HashMap<String, ClassDescriptor>();
|
||||
/** File path of the source text file */
|
||||
private String mOsFilePath;
|
||||
|
||||
/**
|
||||
* Creates a loader with a given file path.
|
||||
* @param osFilePath the OS path of the file to load.
|
||||
* @throws FileNotFoundException if the file is not found.
|
||||
*/
|
||||
WidgetListLoader(String osFilePath) throws FileNotFoundException {
|
||||
mOsFilePath = osFilePath;
|
||||
mReader = new BufferedReader(new FileReader(osFilePath));
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return mOsFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the text file and return true if the file was successfully parsed.
|
||||
* @param monitor
|
||||
*/
|
||||
boolean parseWidgetList(IProgressMonitor monitor) {
|
||||
try {
|
||||
String line;
|
||||
while ((line = mReader.readLine()) != null) {
|
||||
if (line.length() > 0) {
|
||||
char prefix = line.charAt(0);
|
||||
String[] classes = null;
|
||||
ClassDescriptor clazz = null;
|
||||
switch (prefix) {
|
||||
case 'W':
|
||||
classes = line.substring(1).split(" ");
|
||||
clazz = processClass(classes, 0, null /* map */);
|
||||
if (clazz != null) {
|
||||
clazz.setInstantiable(true);
|
||||
mWidgetMap.put(classes[0], clazz);
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
classes = line.substring(1).split(" ");
|
||||
clazz = processClass(classes, 0, null /* map */);
|
||||
if (clazz != null) {
|
||||
clazz.setInstantiable(true);
|
||||
mLayoutMap.put(classes[0], clazz);
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
classes = line.substring(1).split(" ");
|
||||
clazz = processClass(classes, 0, mLayoutParamsMap);
|
||||
if (clazz != null) {
|
||||
clazz.setInstantiable(true);
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
// comment, do nothing
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reconciliate the layout and their layout params
|
||||
postProcess();
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
} finally {
|
||||
try {
|
||||
mReader.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a View class and adds a ViewClassInfo for it in mWidgetMap.
|
||||
* It calls itself recursively to handle super classes which are also Views.
|
||||
* @param classes the inheritance list of the class to process.
|
||||
* @param index the index of the class to process in the <code>classes</code> array.
|
||||
* @param map an optional map in which to put every {@link ClassDescriptor} created.
|
||||
*/
|
||||
private ClassDescriptor processClass(String[] classes, int index,
|
||||
Map<String, ClassDescriptor> map) {
|
||||
if (index >= classes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String fqcn = classes[index];
|
||||
|
||||
if ("java.lang.Object".equals(fqcn)) { //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
|
||||
// check if the ViewInfoClass has not yet been created.
|
||||
if (mMap.containsKey(fqcn)) {
|
||||
return mMap.get(fqcn);
|
||||
}
|
||||
|
||||
// create the custom class.
|
||||
ClassDescriptor clazz = new ClassDescriptor(fqcn);
|
||||
mMap.put(fqcn, clazz);
|
||||
if (map != null) {
|
||||
map.put(fqcn, clazz);
|
||||
}
|
||||
|
||||
// get the super class
|
||||
ClassDescriptor superClass = processClass(classes, index+1, map);
|
||||
if (superClass != null) {
|
||||
clazz.setSuperClass(superClass);
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through the layout params and look for the enclosed class. If the layout params
|
||||
* has no known enclosed type it is dropped.
|
||||
*/
|
||||
private void postProcess() {
|
||||
Collection<ClassDescriptor> params = mLayoutParamsMap.values();
|
||||
|
||||
for (ClassDescriptor param : params) {
|
||||
String fqcn = param.getCanonicalName();
|
||||
|
||||
// get the enclosed name.
|
||||
String enclosed = getEnclosedName(fqcn);
|
||||
|
||||
// look for a match in the layouts. We don't use the layout map as it only contains the
|
||||
// end classes, but in this case we also need to process the layout params for the base
|
||||
// layout classes.
|
||||
ClassDescriptor enclosingType = mMap.get(enclosed);
|
||||
if (enclosingType != null) {
|
||||
param.setEnclosingClass(enclosingType);
|
||||
|
||||
// remove the class from the map, and put it back with the fixed name
|
||||
mMap.remove(fqcn);
|
||||
mMap.put(param.getCanonicalName(), param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getEnclosedName(String fqcn) {
|
||||
int index = fqcn.lastIndexOf('.');
|
||||
return fqcn.substring(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and loads all classes that derive from a given set of super classes.
|
||||
*
|
||||
* @param rootPackage Root package of classes to find. Use an empty string to find everyting.
|
||||
* @param superClasses The super classes of all the classes to find.
|
||||
* @return An hash map which keys are the super classes looked for and which values are
|
||||
* ArrayList of the classes found. The array lists are always created for all the
|
||||
* valid keys, they are simply empty if no deriving class is found for a given
|
||||
* super class.
|
||||
* @throws IOException
|
||||
* @throws InvalidAttributeValueException
|
||||
* @throws ClassFormatError
|
||||
*/
|
||||
public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(String rootPackage,
|
||||
String[] superClasses) throws IOException, InvalidAttributeValueException,
|
||||
ClassFormatError {
|
||||
HashMap<String, ArrayList<IClass>> map = new HashMap<String, ArrayList<IClass>>();
|
||||
|
||||
ArrayList<IClass> list = new ArrayList<IClass>();
|
||||
list.addAll(mWidgetMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEW, list);
|
||||
|
||||
list = new ArrayList<IClass>();
|
||||
list.addAll(mLayoutMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEWGROUP, list);
|
||||
|
||||
list = new ArrayList<IClass>();
|
||||
list.addAll(mLayoutParamsMap.values());
|
||||
map.put(AndroidConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link IClass} by its fully-qualified name.
|
||||
* @param className the fully-qualified name of the class to return.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public IClass getClass(String className) throws ClassNotFoundException {
|
||||
return mMap.get(className);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="PACKAGE"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application android:icon="@drawable/icon" android:label="APPLICATION_NAME">
|
||||
ACTIVITIES
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,7 @@
|
||||
<activity android:name=".ACTIVITY_NAME"
|
||||
android:label="APPLICATION_NAME">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
INTENT_FILTERS
|
||||
</intent-filter>
|
||||
</activity>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,13 @@
|
||||
package PACKAGE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class ACTIVITY_NAME extends Activity {
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<category android:name="android.intent.category.PREFERENCE" />
|
||||
@@ -0,0 +1 @@
|
||||
<string name="STRING_NAME">STRING_CONTENT</string>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
STRINGS
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user